implements PLN-0053
This commit is contained in:
parent
ea8c81368b
commit
bbd588eed4
@ -4,6 +4,7 @@ import java.util.Locale;
|
||||
|
||||
public enum AssetFamilyCatalog {
|
||||
GLYPH_BANK("glyph_bank"),
|
||||
SCENE_BANK("scene_bank"),
|
||||
SOUND_BANK("sound_bank"),
|
||||
UNKNOWN("unknown");
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import java.util.Locale;
|
||||
|
||||
public enum OutputFormatCatalog {
|
||||
GLYPH_INDEXED_V1(AssetFamilyCatalog.GLYPH_BANK, "GLYPH/indexed_v1", "GLYPH/indexed_v1"),
|
||||
SCENE_TILED_V1(AssetFamilyCatalog.SCENE_BANK, "SCENE/tiled_v1", "SCENE/tiled_v1"),
|
||||
SOUND_V1(AssetFamilyCatalog.SOUND_BANK, "SOUND/v1", "SOUND/v1"),
|
||||
UNKNOWN(AssetFamilyCatalog.UNKNOWN, "unknown", "Unknown");
|
||||
|
||||
|
||||
@ -100,6 +100,9 @@ public class PackerAssetWalker {
|
||||
diagnostics.addAll(walkResult.diagnostics());
|
||||
return new PackerWalkResult(walkResult.probeResults(), diagnostics);
|
||||
}
|
||||
case SCENE_BANK -> {
|
||||
return new PackerWalkResult(List.of(), diagnostics);
|
||||
}
|
||||
case UNKNOWN -> {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.WARNING,
|
||||
|
||||
@ -38,6 +38,7 @@ import java.util.stream.Stream;
|
||||
|
||||
public final class FileSystemPackerWorkspaceService implements PackerWorkspaceService {
|
||||
private static final int GLYPH_BANK_COLOR_KEY_RGB565 = 0xF81F;
|
||||
private static final String SCENE_BANK_SUPPORT_FILE = "scene-bank.studio.json";
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
private final PackerWorkspaceFoundation workspaceFoundation;
|
||||
@ -287,6 +288,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
final PackerRegistryEntry entry = workspaceFoundation.allocateIdentity(project, registry, assetRoot);
|
||||
Files.createDirectories(assetRoot);
|
||||
writeManifest(manifestPath, request, entry.assetUuid());
|
||||
writeStudioSupportFiles(assetRoot, request);
|
||||
final PackerRegistryState updated = workspaceFoundation.appendAllocatedEntry(registry, entry);
|
||||
workspaceFoundation.saveRegistry(project, updated);
|
||||
final var runtime = runtimeRegistry.update(project, (snapshot, generation) -> runtimePatchService.afterCreateAsset(
|
||||
@ -667,6 +669,21 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
|
||||
}
|
||||
|
||||
private void writeStudioSupportFiles(Path assetRoot, CreateAssetRequest request) throws IOException {
|
||||
if (request.assetFamily() != AssetFamilyCatalog.SCENE_BANK) {
|
||||
return;
|
||||
}
|
||||
final Map<String, Object> supportFile = new LinkedHashMap<>();
|
||||
supportFile.put("schema_version", 1);
|
||||
supportFile.put("layer_count", 1);
|
||||
supportFile.put("layers", List.of(Map.of(
|
||||
"index", 1,
|
||||
"tilemap", "layer-1.tmx")));
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValue(
|
||||
assetRoot.resolve(SCENE_BANK_SUPPORT_FILE).toFile(),
|
||||
supportFile);
|
||||
}
|
||||
|
||||
private String normalizeRelativeAssetRoot(String candidate) {
|
||||
final String raw = Objects.requireNonNullElse(candidate, "").trim().replace('\\', '/');
|
||||
if (raw.isBlank()) {
|
||||
|
||||
@ -121,7 +121,7 @@ public final class PackerAssetDeclarationParser {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.ERROR,
|
||||
PackerDiagnosticCategory.STRUCTURAL,
|
||||
"Field 'type' must be one of: glyph_bank, palette_bank, sound_bank.",
|
||||
"Field 'type' must be one of: glyph_bank, scene_bank, sound_bank.",
|
||||
manifestPath,
|
||||
true));
|
||||
return null;
|
||||
|
||||
@ -512,6 +512,29 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertTrue(events.stream().anyMatch(event -> event.kind() == PackerEventKind.ACTION_APPLIED));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsSceneBankAssetAndWritesStudioSupportFile() throws Exception {
|
||||
final Path projectRoot = tempDir.resolve("created-scene-bank");
|
||||
final FileSystemPackerWorkspaceService service = service();
|
||||
|
||||
final var result = service.createAsset(new CreateAssetRequest(
|
||||
project(projectRoot),
|
||||
"scenes/overworld",
|
||||
"overworld",
|
||||
AssetFamilyCatalog.SCENE_BANK,
|
||||
OutputFormatCatalog.SCENE_TILED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
false));
|
||||
|
||||
assertEquals(PackerOperationStatus.SUCCESS, result.status());
|
||||
final Path assetRoot = projectRoot.resolve("assets/scenes/overworld");
|
||||
assertTrue(Files.isRegularFile(assetRoot.resolve("asset.json")));
|
||||
assertTrue(Files.isRegularFile(assetRoot.resolve("scene-bank.studio.json")));
|
||||
final var supportFile = MAPPER.readTree(assetRoot.resolve("scene-bank.studio.json").toFile());
|
||||
assertEquals(1, supportFile.path("layer_count").asInt());
|
||||
assertEquals("layer-1.tmx", supportFile.path("layers").get(0).path("tilemap").asText());
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsCreatedAssetThroughRuntimeBackedDetailsWithoutRescanMismatch() throws Exception {
|
||||
final Path projectRoot = tempDir.resolve("created-details");
|
||||
|
||||
@ -67,6 +67,31 @@ final class PackerAssetDeclarationParserTest {
|
||||
assertEquals(128, result.declaration().outputPipelineMetadata().get("samples").path("1").path("length").asInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void parsesSceneBankDeclaration() throws Exception {
|
||||
final Path manifest = tempDir.resolve("scene-asset.json");
|
||||
Files.writeString(manifest, """
|
||||
{
|
||||
"schema_version": 1,
|
||||
"asset_uuid": "uuid-scene",
|
||||
"name": "overworld_scene",
|
||||
"type": "scene_bank",
|
||||
"output": {
|
||||
"format": "SCENE/tiled_v1",
|
||||
"codec": "NONE"
|
||||
},
|
||||
"preload": { "enabled": false }
|
||||
}
|
||||
""");
|
||||
|
||||
final var result = parser.parse(manifest);
|
||||
|
||||
assertTrue(result.valid());
|
||||
assertEquals(AssetFamilyCatalog.SCENE_BANK, result.declaration().assetFamily());
|
||||
assertEquals("SCENE/tiled_v1", result.declaration().outputFormat().displayName());
|
||||
assertEquals(OutputCodecCatalog.NONE, result.declaration().outputCodec());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsNonObjectPipelineMetadata() throws Exception {
|
||||
final Path manifest = tempDir.resolve("asset.json");
|
||||
|
||||
@ -190,10 +190,19 @@ public enum I18n {
|
||||
ASSETS_LABEL_BUILD_PARTICIPATION("assets.label.buildParticipation"),
|
||||
ASSETS_LABEL_ASSET_ID("assets.label.assetId"),
|
||||
ASSETS_LABEL_TYPE("assets.label.type"),
|
||||
ASSETS_LABEL_STUDIO_ROLE("assets.label.studioRole"),
|
||||
ASSETS_LABEL_SCENE_LAYERS("assets.label.sceneLayers"),
|
||||
ASSETS_LABEL_TILEMAPS("assets.label.tilemaps"),
|
||||
ASSETS_LABEL_SUPPORT_FILE("assets.label.supportFile"),
|
||||
ASSETS_TYPE_GLYPH_BANK("assets.type.glyphBank"),
|
||||
ASSETS_TYPE_SCENE_BANK("assets.type.sceneBank"),
|
||||
ASSETS_TYPE_PALETTE_BANK("assets.type.paletteBank"),
|
||||
ASSETS_TYPE_SOUND_BANK("assets.type.soundBank"),
|
||||
ASSETS_TYPE_UNKNOWN("assets.type.unknown"),
|
||||
ASSETS_SPECIALIZATION_NONE("assets.specialization.none"),
|
||||
ASSETS_SPECIALIZATION_TILESET("assets.specialization.tileset"),
|
||||
ASSETS_SPECIALIZATION_SPRITES("assets.specialization.sprites"),
|
||||
ASSETS_SPECIALIZATION_UI("assets.specialization.ui"),
|
||||
ASSETS_LABEL_LOCATION("assets.label.location"),
|
||||
ASSETS_LABEL_BANK("assets.label.bank"),
|
||||
ASSETS_LABEL_TARGET_LOCATION("assets.label.targetLocation"),
|
||||
@ -254,10 +263,12 @@ public enum I18n {
|
||||
ASSETS_ADD_WIZARD_LABEL_NAME("assets.addWizard.label.name"),
|
||||
ASSETS_ADD_WIZARD_LABEL_ROOT("assets.addWizard.label.root"),
|
||||
ASSETS_ADD_WIZARD_LABEL_TYPE("assets.addWizard.label.type"),
|
||||
ASSETS_ADD_WIZARD_LABEL_SPECIALIZATION("assets.addWizard.label.specialization"),
|
||||
ASSETS_ADD_WIZARD_LABEL_FORMAT("assets.addWizard.label.format"),
|
||||
ASSETS_ADD_WIZARD_LABEL_CODEC("assets.addWizard.label.codec"),
|
||||
ASSETS_ADD_WIZARD_LABEL_PRELOAD("assets.addWizard.label.preload"),
|
||||
ASSETS_ADD_WIZARD_PROMPT_TYPE("assets.addWizard.prompt.type"),
|
||||
ASSETS_ADD_WIZARD_PROMPT_SPECIALIZATION("assets.addWizard.prompt.specialization"),
|
||||
ASSETS_ADD_WIZARD_PROMPT_FORMAT("assets.addWizard.prompt.format"),
|
||||
ASSETS_ADD_WIZARD_PROMPT_CODEC("assets.addWizard.prompt.codec"),
|
||||
ASSETS_ADD_WIZARD_ASSETS_ROOT_HINT("assets.addWizard.assetsRootHint"),
|
||||
|
||||
@ -23,6 +23,8 @@ import p.studio.workspaces.assets.details.bank.AssetDetailsBankCompositionContro
|
||||
import p.studio.workspaces.assets.details.contract.AssetDetailsContractControl;
|
||||
import p.studio.workspaces.assets.details.palette.AssetDetailsPaletteOverhaulingControl;
|
||||
import p.studio.workspaces.assets.details.summary.AssetDetailsSummaryControl;
|
||||
import p.studio.workspaces.assets.metadata.AssetStudioMetadataService;
|
||||
import p.studio.workspaces.assets.metadata.AssetStudioMetadataSnapshot;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceAssetAction;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionDetails;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionFile;
|
||||
@ -53,6 +55,7 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
|
||||
private final AssetDetailsContractControl contractControl;
|
||||
private final AssetDetailsBankCompositionControl bankCompositionControl;
|
||||
private final AssetDetailsPaletteOverhaulingControl paletteOverhaulingControl;
|
||||
private final AssetStudioMetadataService studioMetadataService = new AssetStudioMetadataService();
|
||||
private final VBox actionsContent = new VBox(10);
|
||||
private final ScrollPane actionsScroll = new ScrollPane();
|
||||
private final VBox actionsSection;
|
||||
@ -534,6 +537,10 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
|
||||
PackerAssetDetailsDTO details,
|
||||
java.util.List<PackerDiagnosticDTO> diagnostics,
|
||||
java.util.List<PackerAssetActionAvailabilityDTO> actions) {
|
||||
final var baseSummary = AssetListPackerMappings.mapSummary(details.summary());
|
||||
final AssetStudioMetadataSnapshot studioMetadata = studioMetadataService.read(
|
||||
baseSummary.assetRoot(),
|
||||
baseSummary.assetFamily());
|
||||
final java.util.List<PackerDiagnosticDTO> mergedDiagnostics = new java.util.ArrayList<>(details.diagnostics());
|
||||
for (PackerDiagnosticDTO diagnostic : diagnostics) {
|
||||
if (!mergedDiagnostics.contains(diagnostic)) {
|
||||
@ -541,7 +548,17 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
|
||||
}
|
||||
}
|
||||
return new AssetWorkspaceAssetDetails(
|
||||
AssetListPackerMappings.mapSummary(details.summary()),
|
||||
new p.studio.workspaces.assets.messages.AssetWorkspaceAssetSummary(
|
||||
baseSummary.assetReference(),
|
||||
baseSummary.assetName(),
|
||||
baseSummary.state(),
|
||||
baseSummary.buildParticipation(),
|
||||
baseSummary.assetId(),
|
||||
baseSummary.assetFamily(),
|
||||
studioMetadata.glyphSpecialization(),
|
||||
baseSummary.assetRoot(),
|
||||
baseSummary.preload(),
|
||||
baseSummary.hasDiagnostics()),
|
||||
actions.stream()
|
||||
.map(action -> new AssetWorkspaceAssetAction(
|
||||
action.action(),
|
||||
@ -557,6 +574,7 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
|
||||
details.outputPipeline(),
|
||||
mapBankComposition(details.bankComposition()),
|
||||
details.pipelinePalettes(),
|
||||
studioMetadata.sceneBankMetadata(),
|
||||
mergedDiagnostics);
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ import p.studio.controls.forms.StudioFormSection;
|
||||
import p.studio.controls.forms.StudioSection;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.utilities.i18n.I18n;
|
||||
import p.studio.workspaces.assets.metadata.AssetStudioGlyphSpecialization;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceAssetState;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceBuildParticipation;
|
||||
|
||||
@ -114,6 +115,7 @@ public final class AssetDetailsUiSupport {
|
||||
public static String typeLabel(AssetFamilyCatalog assetFamily) {
|
||||
return switch (assetFamily) {
|
||||
case GLYPH_BANK -> Container.i18n().text(I18n.ASSETS_TYPE_GLYPH_BANK);
|
||||
case SCENE_BANK -> Container.i18n().text(I18n.ASSETS_TYPE_SCENE_BANK);
|
||||
case SOUND_BANK -> Container.i18n().text(I18n.ASSETS_TYPE_SOUND_BANK);
|
||||
case UNKNOWN -> Container.i18n().text(I18n.ASSETS_TYPE_UNKNOWN);
|
||||
};
|
||||
@ -122,11 +124,21 @@ public final class AssetDetailsUiSupport {
|
||||
public static String typeChipTone(AssetFamilyCatalog assetFamily) {
|
||||
return switch (assetFamily) {
|
||||
case GLYPH_BANK -> "assets-details-chip-image";
|
||||
case SCENE_BANK -> "assets-details-chip-generic";
|
||||
case SOUND_BANK -> "assets-details-chip-audio";
|
||||
case UNKNOWN -> "assets-details-chip-generic";
|
||||
};
|
||||
}
|
||||
|
||||
public static String specializationLabel(AssetStudioGlyphSpecialization specialization) {
|
||||
return switch (specialization) {
|
||||
case NONE -> Container.i18n().text(I18n.ASSETS_SPECIALIZATION_NONE);
|
||||
case TILESET -> Container.i18n().text(I18n.ASSETS_SPECIALIZATION_TILESET);
|
||||
case SPRITES -> Container.i18n().text(I18n.ASSETS_SPECIALIZATION_SPRITES);
|
||||
case UI -> Container.i18n().text(I18n.ASSETS_SPECIALIZATION_UI);
|
||||
};
|
||||
}
|
||||
|
||||
public static String actionLabel(AssetAction action) {
|
||||
return switch (action) {
|
||||
case REGISTER -> Container.i18n().text(I18n.ASSETS_ACTION_REGISTER);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package p.studio.workspaces.assets.details;
|
||||
|
||||
import p.packer.dtos.PackerAssetSummaryDTO;
|
||||
import p.studio.workspaces.assets.metadata.AssetStudioGlyphSpecialization;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceAssetState;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceAssetSummary;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceBuildParticipation;
|
||||
@ -25,6 +26,7 @@ public final class AssetListPackerMappings {
|
||||
buildParticipation,
|
||||
summary.identity().assetId(),
|
||||
summary.assetFamily(),
|
||||
AssetStudioGlyphSpecialization.NONE,
|
||||
summary.identity().assetRoot(),
|
||||
summary.preloadEnabled(),
|
||||
summary.hasDiagnostics());
|
||||
|
||||
@ -21,6 +21,12 @@ public final class AssetBankCapacityService {
|
||||
final Map<String, Object> safePipeline = Map.copyOf(Objects.requireNonNull(outputPipeline, "outputPipeline"));
|
||||
return switch (safeFamily) {
|
||||
case GLYPH_BANK -> evaluateGlyphBank(artifactCount, safeMetadata);
|
||||
case SCENE_BANK -> new AssetDetailsBankCompositionCapacityState(
|
||||
0.0d,
|
||||
StudioAssetCapacitySeverity.GREEN,
|
||||
false,
|
||||
artifactCount + " support files",
|
||||
"");
|
||||
case SOUND_BANK -> evaluateSoundBank(resolveSoundBankUsedBytes(safePipeline, usedBytes));
|
||||
case UNKNOWN -> new AssetDetailsBankCompositionCapacityState(
|
||||
0.0d,
|
||||
|
||||
@ -8,6 +8,9 @@ import p.studio.lsp.events.StudioWorkspaceEventBus;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.utilities.i18n.I18n;
|
||||
import p.studio.workspaces.assets.details.AssetDetailsUiSupport;
|
||||
import p.studio.workspaces.assets.metadata.AssetStudioGlyphSpecialization;
|
||||
import p.studio.workspaces.assets.metadata.AssetStudioSceneBankMetadata;
|
||||
import p.studio.workspaces.assets.metadata.AssetStudioSceneLayerBinding;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceAssetSummary;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceDetailsViewState;
|
||||
import p.studio.workspaces.assets.messages.events.StudioAssetsDetailsViewStateChangedEvent;
|
||||
@ -53,14 +56,40 @@ public final class AssetDetailsSummaryControl extends VBox implements StudioCont
|
||||
}
|
||||
|
||||
final VBox content = new VBox(8);
|
||||
final AssetStudioSceneBankMetadata sceneBankMetadata = viewState.selectedAssetDetails().sceneBankMetadata();
|
||||
content.getChildren().setAll(
|
||||
AssetDetailsUiSupport.createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_ASSET_ID), summary.assetId() == null ? "—" : String.valueOf(summary.assetId())),
|
||||
AssetDetailsUiSupport.createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_LOCATION), AssetDetailsUiSupport.projectRelativePath(projectReference, summary.assetRoot())));
|
||||
final VBox typeBox = new VBox(6);
|
||||
typeBox.getChildren().add(AssetDetailsUiSupport.createChip(
|
||||
AssetDetailsUiSupport.typeChipTone(summary.assetFamily()),
|
||||
AssetDetailsUiSupport.typeLabel(summary.assetFamily())));
|
||||
if (summary.glyphSpecialization() != AssetStudioGlyphSpecialization.NONE) {
|
||||
typeBox.getChildren().add(AssetDetailsUiSupport.createChip(
|
||||
"assets-details-chip-generic",
|
||||
AssetDetailsUiSupport.specializationLabel(summary.glyphSpecialization())));
|
||||
}
|
||||
content.getChildren().add(AssetDetailsUiSupport.createKeyValueRow(
|
||||
Container.i18n().text(I18n.ASSETS_LABEL_TYPE),
|
||||
AssetDetailsUiSupport.createChip(
|
||||
AssetDetailsUiSupport.typeChipTone(summary.assetFamily()),
|
||||
AssetDetailsUiSupport.typeLabel(summary.assetFamily()))));
|
||||
typeBox));
|
||||
if (summary.glyphSpecialization() != AssetStudioGlyphSpecialization.NONE) {
|
||||
content.getChildren().add(AssetDetailsUiSupport.createKeyValueRow(
|
||||
Container.i18n().text(I18n.ASSETS_LABEL_STUDIO_ROLE),
|
||||
AssetDetailsUiSupport.specializationLabel(summary.glyphSpecialization())));
|
||||
}
|
||||
if (sceneBankMetadata != null) {
|
||||
content.getChildren().add(AssetDetailsUiSupport.createKeyValueRow(
|
||||
Container.i18n().text(I18n.ASSETS_LABEL_SCENE_LAYERS),
|
||||
String.valueOf(sceneBankMetadata.layerCount())));
|
||||
content.getChildren().add(AssetDetailsUiSupport.createKeyValueRow(
|
||||
Container.i18n().text(I18n.ASSETS_LABEL_TILEMAPS),
|
||||
sceneBankMetadata.layerBindings().stream()
|
||||
.map(AssetStudioSceneLayerBinding::tilemap)
|
||||
.collect(java.util.stream.Collectors.joining(", "))));
|
||||
content.getChildren().add(AssetDetailsUiSupport.createKeyValueRow(
|
||||
Container.i18n().text(I18n.ASSETS_LABEL_SUPPORT_FILE),
|
||||
AssetDetailsUiSupport.projectRelativePath(projectReference, sceneBankMetadata.supportFile())));
|
||||
}
|
||||
content.getChildren().add(AssetDetailsUiSupport.createKeyValueRow(
|
||||
Container.i18n().text(I18n.ASSETS_LABEL_REGISTRATION),
|
||||
AssetDetailsUiSupport.createChip(
|
||||
|
||||
@ -113,6 +113,7 @@ public final class AssetListItemControl extends VBox {
|
||||
private String assetRowToneClass(AssetFamilyCatalog assetFamily) {
|
||||
return switch (assetFamily) {
|
||||
case GLYPH_BANK -> "assets-workspace-asset-row-tone-image";
|
||||
case SCENE_BANK -> "assets-workspace-asset-row-tone-generic";
|
||||
case SOUND_BANK -> "assets-workspace-asset-row-tone-audio";
|
||||
default -> "assets-workspace-asset-row-tone-generic";
|
||||
};
|
||||
@ -121,6 +122,7 @@ public final class AssetListItemControl extends VBox {
|
||||
private String assetNameToneClass(AssetFamilyCatalog assetFamily) {
|
||||
return switch (assetFamily) {
|
||||
case GLYPH_BANK -> "assets-workspace-asset-name-tone-image";
|
||||
case SCENE_BANK -> "assets-workspace-asset-name-tone-generic";
|
||||
case SOUND_BANK -> "assets-workspace-asset-name-tone-audio";
|
||||
default -> "assets-workspace-asset-name-tone-generic";
|
||||
};
|
||||
|
||||
@ -4,6 +4,7 @@ import p.packer.dtos.PackerCodecConfigurationFieldDTO;
|
||||
import p.packer.dtos.PackerDiagnosticDTO;
|
||||
import p.packer.messages.assets.OutputCodecCatalog;
|
||||
import p.packer.messages.assets.OutputFormatCatalog;
|
||||
import p.studio.workspaces.assets.metadata.AssetStudioSceneBankMetadata;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -20,6 +21,7 @@ public record AssetWorkspaceAssetDetails(
|
||||
Map<String, Object> outputPipeline,
|
||||
AssetWorkspaceBankCompositionDetails bankComposition,
|
||||
List<Map<String, Object>> pipelinePalettes,
|
||||
AssetStudioSceneBankMetadata sceneBankMetadata,
|
||||
List<PackerDiagnosticDTO> diagnostics) {
|
||||
|
||||
public AssetWorkspaceAssetDetails {
|
||||
|
||||
@ -2,6 +2,7 @@ package p.studio.workspaces.assets.messages;
|
||||
|
||||
import p.packer.messages.AssetReference;
|
||||
import p.packer.messages.assets.AssetFamilyCatalog;
|
||||
import p.studio.workspaces.assets.metadata.AssetStudioGlyphSpecialization;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
@ -13,6 +14,7 @@ public record AssetWorkspaceAssetSummary(
|
||||
AssetWorkspaceBuildParticipation buildParticipation,
|
||||
Integer assetId,
|
||||
AssetFamilyCatalog assetFamily,
|
||||
AssetStudioGlyphSpecialization glyphSpecialization,
|
||||
Path assetRoot,
|
||||
boolean preload,
|
||||
boolean hasDiagnostics) {
|
||||
@ -23,6 +25,7 @@ public record AssetWorkspaceAssetSummary(
|
||||
Objects.requireNonNull(state, "state");
|
||||
Objects.requireNonNull(buildParticipation, "buildParticipation");
|
||||
assetFamily = Objects.requireNonNullElse(assetFamily, AssetFamilyCatalog.UNKNOWN);
|
||||
glyphSpecialization = Objects.requireNonNullElse(glyphSpecialization, AssetStudioGlyphSpecialization.NONE);
|
||||
assetRoot = Objects.requireNonNull(assetRoot, "assetRoot").toAbsolutePath().normalize();
|
||||
if (assetName.isBlank()) {
|
||||
throw new IllegalArgumentException("assetName must not be blank");
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
package p.studio.workspaces.assets.metadata;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public enum AssetStudioGlyphSpecialization {
|
||||
NONE("none"),
|
||||
TILESET("tileset"),
|
||||
SPRITES("sprites"),
|
||||
UI("ui");
|
||||
|
||||
private final String manifestValue;
|
||||
|
||||
AssetStudioGlyphSpecialization(String manifestValue) {
|
||||
this.manifestValue = manifestValue;
|
||||
}
|
||||
|
||||
public String manifestValue() {
|
||||
return manifestValue;
|
||||
}
|
||||
|
||||
public static AssetStudioGlyphSpecialization fromManifestValue(String value) {
|
||||
if (value == null) {
|
||||
return NONE;
|
||||
}
|
||||
final String normalized = value.trim().toLowerCase(Locale.ROOT);
|
||||
for (AssetStudioGlyphSpecialization candidate : values()) {
|
||||
if (candidate.manifestValue.equals(normalized)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
package p.studio.workspaces.assets.metadata;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import p.packer.messages.assets.AssetFamilyCatalog;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public final class AssetStudioMetadataService {
|
||||
public static final String STUDIO_ASSET_METADATA_FILE = "studio.asset.json";
|
||||
public static final String SCENE_BANK_SUPPORT_FILE = "scene-bank.studio.json";
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
public AssetStudioMetadataSnapshot read(Path assetRoot, AssetFamilyCatalog assetFamily) {
|
||||
final Path normalizedAssetRoot = Objects.requireNonNull(assetRoot, "assetRoot").toAbsolutePath().normalize();
|
||||
return new AssetStudioMetadataSnapshot(
|
||||
readGlyphSpecialization(normalizedAssetRoot, assetFamily),
|
||||
readSceneBankMetadata(normalizedAssetRoot, assetFamily));
|
||||
}
|
||||
|
||||
public void writeGlyphSpecialization(Path assetRoot, AssetStudioGlyphSpecialization specialization) throws IOException {
|
||||
final Path metadataPath = Objects.requireNonNull(assetRoot, "assetRoot")
|
||||
.toAbsolutePath()
|
||||
.normalize()
|
||||
.resolve(STUDIO_ASSET_METADATA_FILE);
|
||||
final AssetStudioGlyphSpecialization normalized = Objects.requireNonNullElse(
|
||||
specialization,
|
||||
AssetStudioGlyphSpecialization.NONE);
|
||||
if (normalized == AssetStudioGlyphSpecialization.NONE) {
|
||||
Files.deleteIfExists(metadataPath);
|
||||
return;
|
||||
}
|
||||
final var root = mapper.createObjectNode();
|
||||
root.put("schema_version", 1);
|
||||
root.put("glyph_bank_specialization", normalized.manifestValue());
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValue(metadataPath.toFile(), root);
|
||||
}
|
||||
|
||||
private AssetStudioGlyphSpecialization readGlyphSpecialization(Path assetRoot, AssetFamilyCatalog assetFamily) {
|
||||
if (assetFamily != AssetFamilyCatalog.GLYPH_BANK) {
|
||||
return AssetStudioGlyphSpecialization.NONE;
|
||||
}
|
||||
final Path metadataPath = assetRoot.resolve(STUDIO_ASSET_METADATA_FILE);
|
||||
if (!Files.isRegularFile(metadataPath)) {
|
||||
return AssetStudioGlyphSpecialization.NONE;
|
||||
}
|
||||
try {
|
||||
final JsonNode root = mapper.readTree(metadataPath.toFile());
|
||||
if (root == null || !root.isObject()) {
|
||||
return AssetStudioGlyphSpecialization.NONE;
|
||||
}
|
||||
return AssetStudioGlyphSpecialization.fromManifestValue(root.path("glyph_bank_specialization").asText(null));
|
||||
} catch (IOException ignored) {
|
||||
return AssetStudioGlyphSpecialization.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private AssetStudioSceneBankMetadata readSceneBankMetadata(Path assetRoot, AssetFamilyCatalog assetFamily) {
|
||||
if (assetFamily != AssetFamilyCatalog.SCENE_BANK) {
|
||||
return null;
|
||||
}
|
||||
final Path supportFile = assetRoot.resolve(SCENE_BANK_SUPPORT_FILE);
|
||||
if (!Files.isRegularFile(supportFile)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final JsonNode root = mapper.readTree(supportFile.toFile());
|
||||
if (root == null || !root.isObject()) {
|
||||
return null;
|
||||
}
|
||||
final int layerCount = root.path("layer_count").asInt(0);
|
||||
if (layerCount < 1 || layerCount > 4) {
|
||||
return null;
|
||||
}
|
||||
if (!(root.path("layers") instanceof ArrayNode layersNode) || layersNode.size() != layerCount) {
|
||||
return null;
|
||||
}
|
||||
final List<AssetStudioSceneLayerBinding> bindings = new ArrayList<>();
|
||||
final Set<Integer> indexes = new HashSet<>();
|
||||
for (JsonNode layerNode : layersNode) {
|
||||
final int index = layerNode.path("index").asInt(0);
|
||||
final String tilemap = layerNode.path("tilemap").asText("").trim();
|
||||
if (index < 1 || index > layerCount || tilemap.isBlank() || !indexes.add(index)) {
|
||||
return null;
|
||||
}
|
||||
bindings.add(new AssetStudioSceneLayerBinding(index, tilemap));
|
||||
}
|
||||
bindings.sort(Comparator.comparingInt(AssetStudioSceneLayerBinding::index));
|
||||
return new AssetStudioSceneBankMetadata(layerCount, bindings, supportFile);
|
||||
} catch (IOException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package p.studio.workspaces.assets.metadata;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record AssetStudioMetadataSnapshot(
|
||||
AssetStudioGlyphSpecialization glyphSpecialization,
|
||||
AssetStudioSceneBankMetadata sceneBankMetadata) {
|
||||
|
||||
public AssetStudioMetadataSnapshot {
|
||||
glyphSpecialization = Objects.requireNonNullElse(glyphSpecialization, AssetStudioGlyphSpecialization.NONE);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package p.studio.workspaces.assets.metadata;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public record AssetStudioSceneBankMetadata(
|
||||
int layerCount,
|
||||
List<AssetStudioSceneLayerBinding> layerBindings,
|
||||
Path supportFile) {
|
||||
|
||||
public AssetStudioSceneBankMetadata {
|
||||
layerBindings = List.copyOf(Objects.requireNonNull(layerBindings, "layerBindings"));
|
||||
supportFile = Objects.requireNonNull(supportFile, "supportFile").toAbsolutePath().normalize();
|
||||
if (layerCount < 1 || layerCount > 4) {
|
||||
throw new IllegalArgumentException("layerCount must stay between 1 and 4");
|
||||
}
|
||||
if (layerBindings.size() != layerCount) {
|
||||
throw new IllegalArgumentException("layerBindings must match layerCount");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package p.studio.workspaces.assets.metadata;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record AssetStudioSceneLayerBinding(int index, String tilemap) {
|
||||
public AssetStudioSceneLayerBinding {
|
||||
tilemap = Objects.requireNonNull(tilemap, "tilemap").trim();
|
||||
if (index <= 0) {
|
||||
throw new IllegalArgumentException("index must be positive");
|
||||
}
|
||||
if (tilemap.isBlank()) {
|
||||
throw new IllegalArgumentException("tilemap must not be blank");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -24,6 +24,8 @@ import p.studio.Container;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.utilities.i18n.I18n;
|
||||
import p.studio.workspaces.assets.details.AssetDetailsUiSupport;
|
||||
import p.studio.workspaces.assets.metadata.AssetStudioGlyphSpecialization;
|
||||
import p.studio.workspaces.assets.metadata.AssetStudioMetadataService;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -47,9 +49,11 @@ public final class AddAssetWizard {
|
||||
private final TextField assetRootField = new TextField();
|
||||
private final TextField assetNameField = new TextField();
|
||||
private final ComboBox<AssetFamilyCatalog> assetFamilyCombo = new ComboBox<>();
|
||||
private final ComboBox<AssetStudioGlyphSpecialization> glyphSpecializationCombo = new ComboBox<>();
|
||||
private final ComboBox<OutputFormatCatalog> outputFormatCombo = new ComboBox<>();
|
||||
private final ComboBox<OutputCodecCatalog> outputCodecCombo = new ComboBox<>();
|
||||
private final CheckBox preloadCheckBox = new CheckBox();
|
||||
private final AssetStudioMetadataService studioMetadataService = new AssetStudioMetadataService();
|
||||
|
||||
private int stepIndex;
|
||||
private boolean creating;
|
||||
@ -65,6 +69,7 @@ public final class AddAssetWizard {
|
||||
|
||||
preloadCheckBox.setSelected(false);
|
||||
configureAssetFamilyCombo();
|
||||
configureGlyphSpecializationCombo();
|
||||
configureOutputFormatCombo();
|
||||
configureOutputCodecCombo();
|
||||
renderStep();
|
||||
@ -131,7 +136,34 @@ public final class AddAssetWizard {
|
||||
});
|
||||
assetFamilyCombo.valueProperty().addListener((ignored, oldValue, newValue) -> {
|
||||
if (!Objects.equals(oldValue, newValue)) {
|
||||
if (newValue != AssetFamilyCatalog.GLYPH_BANK) {
|
||||
glyphSpecializationCombo.getSelectionModel().select(AssetStudioGlyphSpecialization.NONE);
|
||||
}
|
||||
refreshOutputFormats();
|
||||
if (stepIndex == 1) {
|
||||
renderStep();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void configureGlyphSpecializationCombo() {
|
||||
glyphSpecializationCombo.setItems(FXCollections.observableArrayList(AssetStudioGlyphSpecialization.values()));
|
||||
glyphSpecializationCombo.setMaxWidth(Double.MAX_VALUE);
|
||||
glyphSpecializationCombo.setPromptText(Container.i18n().text(I18n.ASSETS_ADD_WIZARD_PROMPT_SPECIALIZATION));
|
||||
glyphSpecializationCombo.getSelectionModel().select(AssetStudioGlyphSpecialization.NONE);
|
||||
glyphSpecializationCombo.setCellFactory(ignored -> new javafx.scene.control.ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(AssetStudioGlyphSpecialization item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
setText(empty || item == null ? null : AssetDetailsUiSupport.specializationLabel(item));
|
||||
}
|
||||
});
|
||||
glyphSpecializationCombo.setButtonCell(new javafx.scene.control.ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(AssetStudioGlyphSpecialization item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
setText(empty || item == null ? null : AssetDetailsUiSupport.specializationLabel(item));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -224,17 +256,23 @@ public final class AddAssetWizard {
|
||||
|
||||
final Label nameLabel = new Label(Container.i18n().text(I18n.ASSETS_ADD_WIZARD_LABEL_NAME));
|
||||
final Label typeLabel = new Label(Container.i18n().text(I18n.ASSETS_ADD_WIZARD_LABEL_TYPE));
|
||||
final Label specializationLabel = new Label(Container.i18n().text(I18n.ASSETS_ADD_WIZARD_LABEL_SPECIALIZATION));
|
||||
final Label formatLabel = new Label(Container.i18n().text(I18n.ASSETS_ADD_WIZARD_LABEL_FORMAT));
|
||||
final Label codecLabel = new Label(Container.i18n().text(I18n.ASSETS_ADD_WIZARD_LABEL_CODEC));
|
||||
final Label preloadLabel = new Label(Container.i18n().text(I18n.ASSETS_ADD_WIZARD_LABEL_PRELOAD));
|
||||
final Label noteLabel = new Label(Container.i18n().text(I18n.ASSETS_ADD_WIZARD_NOTE));
|
||||
noteLabel.setWrapText(true);
|
||||
noteLabel.getStyleClass().add("studio-launcher-subtitle");
|
||||
final VBox specializationBox = new VBox(6, specializationLabel, glyphSpecializationCombo);
|
||||
final boolean specializationVisible = selectedFamily() == AssetFamilyCatalog.GLYPH_BANK;
|
||||
specializationBox.setVisible(specializationVisible);
|
||||
specializationBox.setManaged(specializationVisible);
|
||||
|
||||
preloadCheckBox.setText("");
|
||||
stepBody.getChildren().setAll(
|
||||
new VBox(6, nameLabel, assetNameField),
|
||||
new VBox(6, typeLabel, assetFamilyCombo),
|
||||
specializationBox,
|
||||
new VBox(6, formatLabel, outputFormatCombo),
|
||||
new VBox(6, codecLabel, outputCodecCombo),
|
||||
new VBox(6, preloadLabel, preloadCheckBox),
|
||||
@ -360,6 +398,15 @@ public final class AddAssetWizard {
|
||||
private void applyCreateResult(CreateAssetResult createResult) {
|
||||
creating = false;
|
||||
if (createResult.status() == PackerOperationStatus.SUCCESS && createResult.assetReference() != null) {
|
||||
try {
|
||||
persistStudioMetadata(createResult.assetRoot());
|
||||
} catch (IOException exception) {
|
||||
feedbackLabel.setText(exception.getMessage() == null || exception.getMessage().isBlank()
|
||||
? "Unable to persist Studio metadata."
|
||||
: exception.getMessage());
|
||||
renderStep();
|
||||
return;
|
||||
}
|
||||
result.set(createResult.assetReference());
|
||||
stage.close();
|
||||
return;
|
||||
@ -409,6 +456,17 @@ public final class AddAssetWizard {
|
||||
return outputCodecCombo.getValue();
|
||||
}
|
||||
|
||||
private AssetStudioGlyphSpecialization selectedGlyphSpecialization() {
|
||||
return glyphSpecializationCombo.getValue();
|
||||
}
|
||||
|
||||
private void persistStudioMetadata(Path assetRoot) throws IOException {
|
||||
if (selectedFamily() != AssetFamilyCatalog.GLYPH_BANK) {
|
||||
return;
|
||||
}
|
||||
studioMetadataService.writeGlyphSpecialization(assetRoot, selectedGlyphSpecialization());
|
||||
}
|
||||
|
||||
private String normalizedRelativeRoot(String candidate) {
|
||||
final String raw = Objects.requireNonNullElse(candidate, "").trim().replace('\\', '/');
|
||||
if (raw.isBlank()) {
|
||||
|
||||
@ -181,10 +181,19 @@ assets.label.registration=Registration
|
||||
assets.label.buildParticipation=Build Participation
|
||||
assets.label.assetId=Asset ID
|
||||
assets.label.type=Type
|
||||
assets.label.studioRole=Studio Role
|
||||
assets.label.sceneLayers=Scene Layers
|
||||
assets.label.tilemaps=Tilemaps
|
||||
assets.label.supportFile=Support File
|
||||
assets.type.glyphBank=Glyph Bank
|
||||
assets.type.sceneBank=Scene Bank
|
||||
assets.type.paletteBank=Palette Bank
|
||||
assets.type.soundBank=Sound Bank
|
||||
assets.type.unknown=Unknown
|
||||
assets.specialization.none=None
|
||||
assets.specialization.tileset=Tileset
|
||||
assets.specialization.sprites=Sprites
|
||||
assets.specialization.ui=UI
|
||||
assets.label.location=Location
|
||||
assets.label.bank=Bank
|
||||
assets.label.targetLocation=Target Location
|
||||
@ -245,10 +254,12 @@ assets.addWizard.step.summary.description=Confirm the registered asset you are a
|
||||
assets.addWizard.label.name=Asset Name
|
||||
assets.addWizard.label.root=Asset Root
|
||||
assets.addWizard.label.type=Asset Type
|
||||
assets.addWizard.label.specialization=Studio Specialization
|
||||
assets.addWizard.label.format=Output Format
|
||||
assets.addWizard.label.codec=Output Codec
|
||||
assets.addWizard.label.preload=Preload on startup
|
||||
assets.addWizard.prompt.type=Choose asset type
|
||||
assets.addWizard.prompt.specialization=Choose Studio specialization
|
||||
assets.addWizard.prompt.format=Choose output format
|
||||
assets.addWizard.prompt.codec=Choose output codec
|
||||
assets.addWizard.assetsRootHint=Assets root: {0}
|
||||
|
||||
@ -7,6 +7,7 @@ import p.packer.messages.assets.AssetFamilyCatalog;
|
||||
import p.packer.messages.assets.OutputCodecCatalog;
|
||||
import p.packer.messages.assets.OutputFormatCatalog;
|
||||
import p.packer.messages.assets.PackerCodecConfigurationFieldType;
|
||||
import p.studio.workspaces.assets.metadata.AssetStudioGlyphSpecialization;
|
||||
import p.studio.workspaces.assets.messages.*;
|
||||
|
||||
import java.nio.file.Path;
|
||||
@ -114,6 +115,7 @@ final class AssetDetailsBankCompositionCoordinatorTest {
|
||||
List.of(),
|
||||
0L),
|
||||
List.of(),
|
||||
null,
|
||||
List.of());
|
||||
}
|
||||
|
||||
@ -136,6 +138,7 @@ final class AssetDetailsBankCompositionCoordinatorTest {
|
||||
List.of(),
|
||||
0L),
|
||||
List.of(),
|
||||
null,
|
||||
List.of());
|
||||
}
|
||||
|
||||
@ -159,6 +162,7 @@ final class AssetDetailsBankCompositionCoordinatorTest {
|
||||
AssetWorkspaceBuildParticipation.INCLUDED,
|
||||
1,
|
||||
family,
|
||||
AssetStudioGlyphSpecialization.NONE,
|
||||
Path.of("/tmp/bank"),
|
||||
false,
|
||||
false);
|
||||
|
||||
@ -5,6 +5,7 @@ import p.packer.messages.AssetReference;
|
||||
import p.packer.messages.assets.AssetFamilyCatalog;
|
||||
import p.packer.messages.assets.OutputCodecCatalog;
|
||||
import p.packer.messages.assets.OutputFormatCatalog;
|
||||
import p.studio.workspaces.assets.metadata.AssetStudioGlyphSpecialization;
|
||||
import p.studio.workspaces.assets.messages.*;
|
||||
|
||||
import java.nio.file.Path;
|
||||
@ -81,6 +82,7 @@ final class AssetDetailsPaletteOverhaulingCoordinatorTest {
|
||||
AssetWorkspaceBuildParticipation.INCLUDED,
|
||||
1,
|
||||
AssetFamilyCatalog.GLYPH_BANK,
|
||||
AssetStudioGlyphSpecialization.NONE,
|
||||
Path.of("/tmp/bank"),
|
||||
false,
|
||||
false),
|
||||
@ -93,6 +95,7 @@ final class AssetDetailsPaletteOverhaulingCoordinatorTest {
|
||||
Map.of(),
|
||||
new AssetWorkspaceBankCompositionDetails(availableFiles, selectedFiles, 0L),
|
||||
selectedFiles.stream().map(file -> (Map<String, Object>) file.metadata().get("palette")).toList(),
|
||||
null,
|
||||
List.of());
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,76 @@
|
||||
package p.studio.workspaces.assets.metadata;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import p.packer.messages.assets.AssetFamilyCatalog;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
final class AssetStudioMetadataServiceTest {
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
private final AssetStudioMetadataService service = new AssetStudioMetadataService();
|
||||
|
||||
@Test
|
||||
void readsGlyphSpecializationFromStudioMetadataFile() throws Exception {
|
||||
final Path assetRoot = tempDir.resolve("tileset");
|
||||
Files.createDirectories(assetRoot);
|
||||
service.writeGlyphSpecialization(assetRoot, AssetStudioGlyphSpecialization.TILESET);
|
||||
|
||||
final AssetStudioMetadataSnapshot snapshot = service.read(assetRoot, AssetFamilyCatalog.GLYPH_BANK);
|
||||
|
||||
assertEquals(AssetStudioGlyphSpecialization.TILESET, snapshot.glyphSpecialization());
|
||||
assertNull(snapshot.sceneBankMetadata());
|
||||
}
|
||||
|
||||
@Test
|
||||
void readsSceneBankSupportMetadataWhenContractIsValid() throws Exception {
|
||||
final Path assetRoot = tempDir.resolve("scene");
|
||||
Files.createDirectories(assetRoot);
|
||||
Files.writeString(assetRoot.resolve(AssetStudioMetadataService.SCENE_BANK_SUPPORT_FILE), """
|
||||
{
|
||||
"schema_version": 1,
|
||||
"layer_count": 2,
|
||||
"layers": [
|
||||
{ "index": 1, "tilemap": "ground.tmx" },
|
||||
{ "index": 2, "tilemap": "collision.tmx" }
|
||||
]
|
||||
}
|
||||
""");
|
||||
|
||||
final AssetStudioMetadataSnapshot snapshot = service.read(assetRoot, AssetFamilyCatalog.SCENE_BANK);
|
||||
|
||||
assertNotNull(snapshot.sceneBankMetadata());
|
||||
assertEquals(2, snapshot.sceneBankMetadata().layerCount());
|
||||
assertEquals(
|
||||
java.util.List.of("ground.tmx", "collision.tmx"),
|
||||
snapshot.sceneBankMetadata().layerBindings().stream().map(AssetStudioSceneLayerBinding::tilemap).toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsSceneBankSupportMetadataBeyondWaveOneLayerLimit() throws Exception {
|
||||
final Path assetRoot = tempDir.resolve("scene-invalid");
|
||||
Files.createDirectories(assetRoot);
|
||||
Files.writeString(assetRoot.resolve(AssetStudioMetadataService.SCENE_BANK_SUPPORT_FILE), """
|
||||
{
|
||||
"schema_version": 1,
|
||||
"layer_count": 5,
|
||||
"layers": [
|
||||
{ "index": 1, "tilemap": "a.tmx" },
|
||||
{ "index": 2, "tilemap": "b.tmx" },
|
||||
{ "index": 3, "tilemap": "c.tmx" },
|
||||
{ "index": 4, "tilemap": "d.tmx" },
|
||||
{ "index": 5, "tilemap": "e.tmx" }
|
||||
]
|
||||
}
|
||||
""");
|
||||
|
||||
final AssetStudioMetadataSnapshot snapshot = service.read(assetRoot, AssetFamilyCatalog.SCENE_BANK);
|
||||
|
||||
assertNull(snapshot.sceneBankMetadata());
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user