asset details (WIP)
This commit is contained in:
parent
64df57a774
commit
9c657b0450
@ -86,12 +86,14 @@ Required baseline fields:
|
||||
Optional baseline field:
|
||||
|
||||
- `output.metadata`
|
||||
- `output.pipeline`
|
||||
|
||||
Rules:
|
||||
|
||||
- `output.format` defines the semantic/runtime format contract;
|
||||
- `output.codec` defines storage/extraction behavior;
|
||||
- `output.metadata` carries runtime-relevant format-specific detail;
|
||||
- `output.pipeline` carries pipeline-injected metadata kept separate at authoring time;
|
||||
- codec must remain explicit and must not be hidden inside format naming.
|
||||
|
||||
## Metadata Source Segmentation and Runtime Sink
|
||||
@ -101,6 +103,7 @@ Rules:
|
||||
Rules:
|
||||
|
||||
- declaration-time metadata may come from multiple sources under the asset contract (for example, format metadata, codec-related metadata, and build/pipeline-derived declarations);
|
||||
- `output.pipeline` may carry nested pipeline-derived metadata objects;
|
||||
- this segmentation exists for authoring clarity and does not define multiple runtime sinks;
|
||||
- runtime consumers must read effective metadata from the runtime asset entry metadata sink (`AssetEntry.metadata`);
|
||||
- convergence/normalization behavior is normative in the build artifact specification.
|
||||
|
||||
@ -24,4 +24,6 @@ public interface PackerWorkspaceService {
|
||||
UpdateAssetContractResponse updateAssetContract(UpdateAssetContractRequest request);
|
||||
|
||||
ApplyBankCompositionResponse applyBankComposition(ApplyBankCompositionRequest request);
|
||||
|
||||
ApplyPaletteOverhaulingResponse applyPaletteOverhauling(ApplyPaletteOverhaulingRequest request);
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ public record PackerAssetDetailsDTO(
|
||||
Map<OutputCodecCatalog, List<PackerCodecConfigurationFieldDTO>> codecConfigurationFieldsByCodec,
|
||||
List<PackerCodecConfigurationFieldDTO> metadataFields,
|
||||
PackerBankCompositionDetailsDTO bankComposition,
|
||||
List<Map<String, Object>> pipelinePalettes,
|
||||
List<PackerDiagnosticDTO> diagnostics) {
|
||||
|
||||
public PackerAssetDetailsDTO {
|
||||
@ -25,6 +26,7 @@ public record PackerAssetDetailsDTO(
|
||||
codecConfigurationFieldsByCodec = Map.copyOf(Objects.requireNonNull(codecConfigurationFieldsByCodec, "codecConfigurationFieldsByCodec"));
|
||||
metadataFields = List.copyOf(Objects.requireNonNull(metadataFields, "metadataFields"));
|
||||
bankComposition = Objects.requireNonNull(bankComposition, "bankComposition");
|
||||
pipelinePalettes = List.copyOf(Objects.requireNonNull(pipelinePalettes, "pipelinePalettes"));
|
||||
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
package p.packer.messages;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public record ApplyPaletteOverhaulingRequest(
|
||||
PackerProjectContext project,
|
||||
AssetReference assetReference,
|
||||
List<Map<String, Object>> selectedPalettes) {
|
||||
|
||||
public ApplyPaletteOverhaulingRequest {
|
||||
Objects.requireNonNull(project, "project");
|
||||
Objects.requireNonNull(assetReference, "assetReference");
|
||||
selectedPalettes = List.copyOf(Objects.requireNonNull(selectedPalettes, "selectedPalettes").stream()
|
||||
.map(palette -> Map.copyOf(new LinkedHashMap<>(Objects.requireNonNull(palette, "palette"))))
|
||||
.toList());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package p.packer.messages;
|
||||
|
||||
public record ApplyPaletteOverhaulingResponse(
|
||||
boolean success,
|
||||
String errorMessage) {
|
||||
}
|
||||
@ -1,10 +1,12 @@
|
||||
package p.packer.models;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import p.packer.messages.assets.AssetFamilyCatalog;
|
||||
import p.packer.messages.assets.OutputCodecCatalog;
|
||||
import p.packer.messages.assets.OutputFormatCatalog;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -18,6 +20,7 @@ public record PackerAssetDeclaration(
|
||||
OutputFormatCatalog outputFormat,
|
||||
OutputCodecCatalog outputCodec,
|
||||
Map<String, String> outputMetadata,
|
||||
Map<String, JsonNode> outputPipelineMetadata,
|
||||
boolean preloadEnabled) {
|
||||
|
||||
public PackerAssetDeclaration {
|
||||
@ -31,8 +34,20 @@ public record PackerAssetDeclaration(
|
||||
outputFormat = Objects.requireNonNull(outputFormat, "outputFormat");
|
||||
outputCodec = Objects.requireNonNull(outputCodec, "outputCodec");
|
||||
outputMetadata = Map.copyOf(Objects.requireNonNull(outputMetadata, "outputMetadata"));
|
||||
outputPipelineMetadata = immutablePipelineMetadata(outputPipelineMetadata);
|
||||
if (StringUtils.isBlank(assetUuid) || StringUtils.isBlank(name)) {
|
||||
throw new IllegalArgumentException("declaration fields must not be blank");
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, JsonNode> immutablePipelineMetadata(Map<String, JsonNode> pipelineMetadata) {
|
||||
final Map<String, JsonNode> sanitized = new LinkedHashMap<>();
|
||||
Objects.requireNonNull(pipelineMetadata, "outputPipelineMetadata").forEach((key, value) -> {
|
||||
if (key == null || key.isBlank() || value == null) {
|
||||
return;
|
||||
}
|
||||
sanitized.put(key.trim(), value.deepCopy());
|
||||
});
|
||||
return Map.copyOf(sanitized);
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ public record PackerAssetDetails(
|
||||
Map<OutputCodecCatalog, List<PackerCodecConfigurationField>> codecConfigurationFieldsByCodec,
|
||||
List<PackerCodecConfigurationField> metadataFields,
|
||||
PackerBankCompositionDetails bankComposition,
|
||||
List<Map<String, Object>> pipelinePalettes,
|
||||
List<PackerDiagnostic> diagnostics) {
|
||||
|
||||
public PackerAssetDetails {
|
||||
@ -25,6 +26,7 @@ public record PackerAssetDetails(
|
||||
codecConfigurationFieldsByCodec = Map.copyOf(Objects.requireNonNull(codecConfigurationFieldsByCodec, "codecConfigurationFieldsByCodec"));
|
||||
metadataFields = List.copyOf(Objects.requireNonNull(metadataFields, "metadataFields"));
|
||||
bankComposition = Objects.requireNonNull(bankComposition, "bankComposition");
|
||||
pipelinePalettes = List.copyOf(Objects.requireNonNull(pipelinePalettes, "pipelinePalettes"));
|
||||
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package p.packer.services;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import p.packer.PackerWorkspacePaths;
|
||||
import p.packer.PackerWorkspaceService;
|
||||
@ -541,7 +542,8 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
manifest.put("type", request.assetFamily().manifestType());
|
||||
manifest.put("output", Map.of(
|
||||
"format", request.outputFormat().manifestValue(),
|
||||
"codec", request.outputCodec().manifestValue()));
|
||||
"codec", request.outputCodec().manifestValue(),
|
||||
"pipeline", Map.of()));
|
||||
manifest.put("preload", Map.of("enabled", request.preloadEnabled()));
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
|
||||
}
|
||||
@ -665,6 +667,13 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
return writeCoordinator.execute(project, () -> applyBankCompositionInWriteLane(safeRequest));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplyPaletteOverhaulingResponse applyPaletteOverhauling(final ApplyPaletteOverhaulingRequest request) {
|
||||
final ApplyPaletteOverhaulingRequest safeRequest = Objects.requireNonNull(request, "request");
|
||||
final PackerProjectContext project = safeRequest.project();
|
||||
return writeCoordinator.execute(project, () -> applyPaletteOverhaulingInWriteLane(safeRequest));
|
||||
}
|
||||
|
||||
private UpdateAssetContractResponse updateAssetContractInWriteLane(UpdateAssetContractRequest request) {
|
||||
final PackerProjectContext project = request.project();
|
||||
workspaceFoundation.initWorkspace(new InitWorkspaceRequest(project));
|
||||
@ -772,6 +781,55 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
}
|
||||
}
|
||||
|
||||
private ApplyPaletteOverhaulingResponse applyPaletteOverhaulingInWriteLane(ApplyPaletteOverhaulingRequest request) {
|
||||
final PackerProjectContext project = request.project();
|
||||
workspaceFoundation.initWorkspace(new InitWorkspaceRequest(project));
|
||||
final PackerRuntimeSnapshot snapshot = runtimeRegistry.getOrLoad(project).snapshot();
|
||||
final PackerDeleteAssetEvaluation evaluation = actionReadService.evaluateDelete(snapshot, project, request.assetReference());
|
||||
if (!evaluation.canDelete()) {
|
||||
return new ApplyPaletteOverhaulingResponse(
|
||||
false,
|
||||
Objects.requireNonNullElse(evaluation.reason(), "Asset palette overhauling cannot be updated."));
|
||||
}
|
||||
if (request.selectedPalettes().isEmpty()) {
|
||||
return new ApplyPaletteOverhaulingResponse(false, "At least one palette must be selected.");
|
||||
}
|
||||
|
||||
final Path assetRoot = evaluation.resolved().assetRoot();
|
||||
final Path manifestPath = assetRoot.resolve("asset.json");
|
||||
if (!Files.isRegularFile(manifestPath)) {
|
||||
return new ApplyPaletteOverhaulingResponse(false, "asset.json was not found for the requested asset root.");
|
||||
}
|
||||
|
||||
final ObjectNode manifest;
|
||||
try {
|
||||
final JsonNode rawManifest = mapper.readTree(manifestPath.toFile());
|
||||
if (!(rawManifest instanceof ObjectNode objectNode)) {
|
||||
return new ApplyPaletteOverhaulingResponse(false, "asset.json must contain a JSON object at the root.");
|
||||
}
|
||||
manifest = objectNode;
|
||||
} catch (IOException exception) {
|
||||
return new ApplyPaletteOverhaulingResponse(false, "Unable to read asset manifest: " + exception.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
patchManifestPipelinePalettes(manifest, request);
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
|
||||
final var runtime = runtimeRegistry.update(project, (currentSnapshot, generation) -> runtimePatchService.afterUpdateAssetContract(
|
||||
currentSnapshot,
|
||||
generation,
|
||||
assetRoot,
|
||||
manifestPath,
|
||||
evaluation.resolved().registryEntry()));
|
||||
saveRuntimeCache(project, runtime.snapshot());
|
||||
return new ApplyPaletteOverhaulingResponse(true, null);
|
||||
} catch (IOException exception) {
|
||||
return new ApplyPaletteOverhaulingResponse(false, "Unable to update asset palette overhauling: " + exception.getMessage());
|
||||
} catch (RuntimeException exception) {
|
||||
return new ApplyPaletteOverhaulingResponse(false, "Unable to update runtime snapshot: " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private OutputFormatCatalog resolveManifestOutputFormat(ObjectNode manifest) {
|
||||
final JsonNode outputNode = manifest.get("output");
|
||||
if (!(outputNode instanceof ObjectNode outputObject)) {
|
||||
@ -832,6 +890,41 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
}
|
||||
}
|
||||
|
||||
private void patchManifestPipelinePalettes(ObjectNode manifest, ApplyPaletteOverhaulingRequest request) {
|
||||
final ObjectNode outputNode = mutableObject(manifest, "output");
|
||||
final ObjectNode pipelineNode = mutableObject(outputNode, "pipeline");
|
||||
final ArrayNode palettesNode = pipelineNode.putArray("palettes");
|
||||
for (Map<String, Object> palette : request.selectedPalettes()) {
|
||||
final List<Integer> originalArgb8888 = integerList(palette.get("originalArgb8888"));
|
||||
final List<Integer> convertedRgb565 = integerList(palette.get("convertedRgb565"));
|
||||
if (originalArgb8888.isEmpty() || convertedRgb565.isEmpty()) {
|
||||
throw new IllegalArgumentException("Each selected palette must contain originalArgb8888 and convertedRgb565 entries.");
|
||||
}
|
||||
final ObjectNode paletteNode = palettesNode.addObject();
|
||||
final ArrayNode originalNode = paletteNode.putArray("originalArgb8888");
|
||||
originalArgb8888.forEach(originalNode::add);
|
||||
final ArrayNode convertedNode = paletteNode.putArray("convertedRgb565");
|
||||
convertedRgb565.forEach(convertedNode::add);
|
||||
}
|
||||
if (palettesNode.isEmpty()) {
|
||||
throw new IllegalArgumentException("At least one palette must be selected.");
|
||||
}
|
||||
}
|
||||
|
||||
private List<Integer> integerList(Object value) {
|
||||
if (!(value instanceof Iterable<?> iterable)) {
|
||||
return List.of();
|
||||
}
|
||||
final List<Integer> numbers = new ArrayList<>();
|
||||
for (Object item : iterable) {
|
||||
if (!(item instanceof Number number)) {
|
||||
return List.of();
|
||||
}
|
||||
numbers.add(number.intValue());
|
||||
}
|
||||
return List.copyOf(numbers);
|
||||
}
|
||||
|
||||
private String contractFingerprint(ObjectNode manifest) {
|
||||
final JsonNode outputNode = manifest.path("output");
|
||||
if (!(outputNode instanceof ObjectNode outputObject)) {
|
||||
|
||||
@ -50,6 +50,7 @@ public final class PackerAssetDeclarationParser {
|
||||
final var outputFormat = requiredOutputFormat(root.path("output"), diagnostics, manifestPath);
|
||||
final var outputCodec = requiredOutputCodec(root.path("output"), diagnostics, manifestPath);
|
||||
final var outputMetadata = optionalOutputMetadata(root.path("output"), diagnostics, manifestPath);
|
||||
final var outputPipelineMetadata = optionalOutputPipelineMetadata(root.path("output"), diagnostics, manifestPath);
|
||||
final var preloadEnabled = requiredBoolean(root.path("preload"), "enabled", diagnostics, manifestPath);
|
||||
|
||||
if (schemaVersion != null && !SUPPORTED_SCHEMA_VERSIONS.contains(schemaVersion)) {
|
||||
@ -75,6 +76,7 @@ public final class PackerAssetDeclarationParser {
|
||||
outputFormat,
|
||||
outputCodec,
|
||||
outputMetadata,
|
||||
outputPipelineMetadata,
|
||||
preloadEnabled),
|
||||
diagnostics);
|
||||
}
|
||||
@ -228,6 +230,51 @@ public final class PackerAssetDeclarationParser {
|
||||
return Map.copyOf(metadata);
|
||||
}
|
||||
|
||||
private Map<String, JsonNode> optionalOutputPipelineMetadata(
|
||||
final JsonNode node,
|
||||
final List<PackerDiagnostic> diagnostics,
|
||||
final Path manifestPath) {
|
||||
final JsonNode pipelineNode = node.path("pipeline");
|
||||
if (pipelineNode.isMissingNode() || pipelineNode.isNull()) {
|
||||
return Map.of();
|
||||
}
|
||||
if (!pipelineNode.isObject()) {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.ERROR,
|
||||
PackerDiagnosticCategory.STRUCTURAL,
|
||||
"Field 'output.pipeline' must be a JSON object.",
|
||||
manifestPath,
|
||||
true));
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
final Map<String, JsonNode> pipelineMetadata = new LinkedHashMap<>();
|
||||
pipelineNode.fields().forEachRemaining(entry -> {
|
||||
final String key = Objects.requireNonNullElse(entry.getKey(), "").trim();
|
||||
if (key.isBlank()) {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.ERROR,
|
||||
PackerDiagnosticCategory.STRUCTURAL,
|
||||
"Field 'output.pipeline' has an invalid empty key.",
|
||||
manifestPath,
|
||||
true));
|
||||
return;
|
||||
}
|
||||
final JsonNode valueNode = entry.getValue();
|
||||
if (valueNode == null) {
|
||||
diagnostics.add(new PackerDiagnostic(
|
||||
PackerDiagnosticSeverity.ERROR,
|
||||
PackerDiagnosticCategory.STRUCTURAL,
|
||||
"Field 'output.pipeline' values must be valid JSON values.",
|
||||
manifestPath,
|
||||
true));
|
||||
return;
|
||||
}
|
||||
pipelineMetadata.put(key, valueNode.deepCopy());
|
||||
});
|
||||
return Map.copyOf(pipelineMetadata);
|
||||
}
|
||||
|
||||
private void rejectLegacyInputs(
|
||||
final JsonNode node,
|
||||
final List<PackerDiagnostic> diagnostics,
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
package p.packer.services;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import p.packer.PackerWorkspacePaths;
|
||||
import p.packer.messages.*;
|
||||
import p.packer.messages.assets.*;
|
||||
@ -80,6 +83,7 @@ public final class PackerAssetDetailsService {
|
||||
outputContract.codecConfigurationFieldsByCodec(),
|
||||
metadataFields(outputContract.metadataFields(), declaration.outputMetadata()),
|
||||
resolveBankCompositionDetails(snapshot, runtimeAsset, declaration),
|
||||
resolvePipelinePalettes(declaration),
|
||||
diagnostics);
|
||||
return new GetAssetDetailsResult(
|
||||
diagnostics.stream().anyMatch(PackerDiagnostic::blocking) ? PackerOperationStatus.PARTIAL : PackerOperationStatus.SUCCESS,
|
||||
@ -119,6 +123,7 @@ public final class PackerAssetDetailsService {
|
||||
Map.of(OutputCodecCatalog.NONE, List.of()),
|
||||
List.of(),
|
||||
PackerBankCompositionDetails.EMPTY,
|
||||
List.of(),
|
||||
diagnostics);
|
||||
return new GetAssetDetailsResult(
|
||||
PackerOperationStatus.FAILED,
|
||||
@ -160,6 +165,49 @@ public final class PackerAssetDetailsService {
|
||||
.toList();
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> resolvePipelinePalettes(PackerAssetDeclaration declaration) {
|
||||
final JsonNode palettesNode = declaration.outputPipelineMetadata().get("palettes");
|
||||
if (!(palettesNode instanceof ArrayNode palettesArray)) {
|
||||
return List.of();
|
||||
}
|
||||
final List<Map<String, Object>> palettes = new ArrayList<>();
|
||||
for (JsonNode paletteNode : palettesArray) {
|
||||
final Map<String, Object> normalized = palettePayloadFromNode(paletteNode);
|
||||
if (!normalized.isEmpty()) {
|
||||
palettes.add(normalized);
|
||||
}
|
||||
}
|
||||
return List.copyOf(palettes);
|
||||
}
|
||||
|
||||
private Map<String, Object> palettePayloadFromNode(JsonNode paletteNode) {
|
||||
if (!(paletteNode instanceof ObjectNode paletteObject)) {
|
||||
return Map.of();
|
||||
}
|
||||
final JsonNode originalNode = paletteObject.path("originalArgb8888");
|
||||
final JsonNode convertedNode = paletteObject.path("convertedRgb565");
|
||||
if (!originalNode.isArray() || !convertedNode.isArray()) {
|
||||
return Map.of();
|
||||
}
|
||||
final List<Integer> originalArgb8888 = new ArrayList<>();
|
||||
for (JsonNode colorNode : originalNode) {
|
||||
if (!colorNode.canConvertToInt()) {
|
||||
return Map.of();
|
||||
}
|
||||
originalArgb8888.add(colorNode.intValue());
|
||||
}
|
||||
final List<Integer> convertedRgb565 = new ArrayList<>();
|
||||
for (JsonNode colorNode : convertedNode) {
|
||||
if (!colorNode.canConvertToInt()) {
|
||||
return Map.of();
|
||||
}
|
||||
convertedRgb565.add(colorNode.intValue());
|
||||
}
|
||||
return Map.of(
|
||||
"originalArgb8888", List.copyOf(originalArgb8888),
|
||||
"convertedRgb565", List.copyOf(convertedRgb565));
|
||||
}
|
||||
|
||||
private Optional<PackerBankCompositionFile> resolveSelectedBankFile(
|
||||
Path assetRoot,
|
||||
String relativePath,
|
||||
|
||||
@ -32,6 +32,7 @@ public final class PackerReadMessageMapper {
|
||||
toCodecConfigurationFieldsByCodecDTO(details.codecConfigurationFieldsByCodec()),
|
||||
toCodecConfigurationFieldDTOs(details.metadataFields()),
|
||||
toBankCompositionDetailsDTO(details.bankComposition()),
|
||||
details.pipelinePalettes().stream().map(PackerReadMessageMapper::normalizeMetadata).toList(),
|
||||
toDiagnosticDTOs(details.diagnostics()));
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package p.packer.repositories;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
@ -77,6 +78,7 @@ final class PackerRuntimeAssetMaterializerTest {
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
metadata,
|
||||
Map.<String, JsonNode>of(),
|
||||
true);
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package p.packer.services;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import p.packer.events.PackerEvent;
|
||||
@ -106,6 +107,7 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertTrue(Files.isRegularFile(projectRoot.resolve("assets/ui/new-atlas/asset.json")));
|
||||
final String manifestJson = Files.readString(projectRoot.resolve("assets/ui/new-atlas/asset.json"));
|
||||
assertTrue(manifestJson.contains("\"asset_uuid\""));
|
||||
assertTrue(manifestJson.contains("\"pipeline\""));
|
||||
|
||||
final var snapshot = service.listAssets(new ListAssetsRequest(project(projectRoot)));
|
||||
assertEquals(1, snapshot.assets().size());
|
||||
@ -216,6 +218,33 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertEquals("mono", manifest.path("output").path("codec_configuration").path("palette").asText());
|
||||
assertEquals("16x16", manifest.path("output").path("metadata").path("tile_size").asText());
|
||||
assertEquals("1", manifest.path("output").path("metadata").path("channels").asText());
|
||||
assertTrue(manifest.path("output").path("pipeline").isMissingNode() || manifest.path("output").path("pipeline").isObject());
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateAssetContractPreservesPipelineMetadataUnderOutput() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("update-contract-pipeline"));
|
||||
final Path manifestPath = projectRoot.resolve("assets/ui/atlas/asset.json");
|
||||
final ObjectNode manifest = (ObjectNode) MAPPER.readTree(manifestPath.toFile());
|
||||
final ObjectNode pipelineNode = ((ObjectNode) manifest.path("output")).putObject("pipeline");
|
||||
pipelineNode.putObject("samples").putObject("1").put("offset", 0).put("length", 64);
|
||||
pipelineNode.put("normalized", true);
|
||||
MAPPER.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
|
||||
|
||||
final FileSystemPackerWorkspaceService service = service();
|
||||
final var result = service.updateAssetContract(new UpdateAssetContractRequest(
|
||||
project(projectRoot),
|
||||
AssetReference.forAssetId(1),
|
||||
false,
|
||||
OutputCodecCatalog.NONE,
|
||||
Map.of("NONE:packMode", "tight"),
|
||||
Map.of("tile_size", "16x16")));
|
||||
|
||||
assertTrue(result.success());
|
||||
|
||||
final var updatedManifest = MAPPER.readTree(manifestPath.toFile());
|
||||
assertTrue(updatedManifest.path("output").path("pipeline").path("normalized").asBoolean());
|
||||
assertEquals(64, updatedManifest.path("output").path("pipeline").path("samples").path("1").path("length").asInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -345,6 +374,44 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertEquals(1, loader.loadCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyPaletteOverhaulingWritesPipelinePalettesInOrder() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("apply-palette-overhauling"));
|
||||
final FileSystemPackerWorkspaceService service = service();
|
||||
|
||||
final var response = service.applyPaletteOverhauling(new ApplyPaletteOverhaulingRequest(
|
||||
project(projectRoot),
|
||||
AssetReference.forAssetId(1),
|
||||
List.of(
|
||||
Map.of(
|
||||
"originalArgb8888", List.of(0xFFFF0000, 0xFF00FF00),
|
||||
"convertedRgb565", List.of(0xF800, 0x07E0)),
|
||||
Map.of(
|
||||
"originalArgb8888", List.of(0xFF0000FF),
|
||||
"convertedRgb565", List.of(0x001F)))));
|
||||
|
||||
assertTrue(response.success());
|
||||
|
||||
final var manifest = MAPPER.readTree(projectRoot.resolve("assets/ui/atlas/asset.json").toFile());
|
||||
assertEquals(2, manifest.path("output").path("pipeline").path("palettes").size());
|
||||
assertEquals(0xFFFF0000, manifest.path("output").path("pipeline").path("palettes").get(0).path("originalArgb8888").get(0).asInt());
|
||||
assertEquals(0x001F, manifest.path("output").path("pipeline").path("palettes").get(1).path("convertedRgb565").get(0).asInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyPaletteOverhaulingFailsWhenSelectionIsEmpty() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("apply-palette-overhauling-empty"));
|
||||
final FileSystemPackerWorkspaceService service = service();
|
||||
|
||||
final var response = service.applyPaletteOverhauling(new ApplyPaletteOverhaulingRequest(
|
||||
project(projectRoot),
|
||||
AssetReference.forAssetId(1),
|
||||
List.of()));
|
||||
|
||||
assertFalse(response.success());
|
||||
assertTrue(response.errorMessage().contains("At least one palette"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deepSyncPreservesContractDrivenBankInvalidationAfterPatchUpdate() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("deep-sync-bank-composition"));
|
||||
|
||||
@ -10,6 +10,7 @@ import p.packer.testing.PackerFixtureLocator;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ -31,9 +32,65 @@ final class PackerAssetDeclarationParserTest {
|
||||
assertEquals(AssetFamilyCatalog.TILE_BANK, result.declaration().assetFamily());
|
||||
assertEquals("TILES/indexed_v1", result.declaration().outputFormat().displayName());
|
||||
assertEquals(OutputCodecCatalog.NONE, result.declaration().outputCodec());
|
||||
assertEquals(Map.of(), result.declaration().outputPipelineMetadata());
|
||||
assertTrue(result.declaration().preloadEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
void parsesPipelineMetadataAsDedicatedOutputObject() throws Exception {
|
||||
final Path manifest = tempDir.resolve("asset.json");
|
||||
Files.writeString(manifest, """
|
||||
{
|
||||
"schema_version": 1,
|
||||
"asset_uuid": "uuid-pipeline",
|
||||
"name": "pipeline_asset",
|
||||
"type": "sound_bank",
|
||||
"output": {
|
||||
"format": "SOUND/v1",
|
||||
"codec": "NONE",
|
||||
"pipeline": {
|
||||
"samples": {
|
||||
"1": { "offset": 0, "length": 128 }
|
||||
},
|
||||
"normalized": true
|
||||
}
|
||||
},
|
||||
"preload": { "enabled": true }
|
||||
}
|
||||
""");
|
||||
|
||||
final var result = parser.parse(manifest);
|
||||
|
||||
assertTrue(result.valid());
|
||||
assertEquals(2, result.declaration().outputPipelineMetadata().size());
|
||||
assertTrue(result.declaration().outputPipelineMetadata().get("normalized").asBoolean());
|
||||
assertEquals(128, result.declaration().outputPipelineMetadata().get("samples").path("1").path("length").asInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsNonObjectPipelineMetadata() throws Exception {
|
||||
final Path manifest = tempDir.resolve("asset.json");
|
||||
Files.writeString(manifest, """
|
||||
{
|
||||
"schema_version": 1,
|
||||
"asset_uuid": "uuid-pipeline",
|
||||
"name": "pipeline_asset",
|
||||
"type": "tile_bank",
|
||||
"output": {
|
||||
"format": "TILES/indexed_v1",
|
||||
"codec": "NONE",
|
||||
"pipeline": []
|
||||
},
|
||||
"preload": { "enabled": true }
|
||||
}
|
||||
""");
|
||||
|
||||
final var result = parser.parse(manifest);
|
||||
|
||||
assertFalse(result.valid());
|
||||
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("output.pipeline")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsMalformedJsonWithStructuralDiagnostic() {
|
||||
final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/invalid-malformed/assets/bad/asset.json"));
|
||||
|
||||
@ -49,10 +49,35 @@ final class PackerAssetDetailsServiceTest {
|
||||
assertEquals(List.of(), result.details().codecConfigurationFieldsByCodec().get(OutputCodecCatalog.NONE));
|
||||
assertNotNull(result.details().bankComposition());
|
||||
assertTrue(result.details().bankComposition().selectedFiles().isEmpty());
|
||||
assertTrue(result.details().pipelinePalettes().isEmpty());
|
||||
assertTrue(result.diagnostics().stream().noneMatch(diagnostic -> diagnostic.blocking()));
|
||||
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("Output metadata for tile bank cannot be empty")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void exposesPipelinePalettesFromOutputPipelineMetadata() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("managed-pipeline-palettes"));
|
||||
final Path manifestPath = projectRoot.resolve("assets/ui/atlas/asset.json");
|
||||
|
||||
final ObjectMapper mapper = new ObjectMapper();
|
||||
final ObjectNode manifest = (ObjectNode) mapper.readTree(manifestPath.toFile());
|
||||
final ObjectNode pipeline = ((ObjectNode) manifest.path("output")).putObject("pipeline");
|
||||
final var palettes = pipeline.putArray("palettes");
|
||||
palettes.addObject()
|
||||
.putArray("originalArgb8888").add(0xFFFF0000).add(0xFF00FF00);
|
||||
((ObjectNode) palettes.get(0))
|
||||
.putArray("convertedRgb565").add(0xF800).add(0x07E0);
|
||||
mapper.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
|
||||
|
||||
final PackerAssetDetailsService service = service();
|
||||
final var result = service.getAssetDetails(new GetAssetDetailsRequest(project(projectRoot), AssetReference.forAssetId(1)));
|
||||
|
||||
assertEquals(1, result.details().pipelinePalettes().size());
|
||||
assertEquals(
|
||||
List.of(0xFFFF0000, 0xFF00FF00),
|
||||
result.details().pipelinePalettes().getFirst().get("originalArgb8888"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void projectsBankCompositionAvailableAndSelectedFiles() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("managed-bank-composition"));
|
||||
|
||||
@ -203,7 +203,7 @@ public abstract class StudioDualListView<T> extends HBox {
|
||||
return;
|
||||
}
|
||||
final String text = indexed
|
||||
? (getIndex() + 1) + ". " + itemText(item)
|
||||
? getIndex() + ". " + itemText(item)
|
||||
: itemText(item);
|
||||
setText(text);
|
||||
setGraphic(null);
|
||||
@ -259,7 +259,7 @@ public abstract class StudioDualListView<T> extends HBox {
|
||||
}
|
||||
|
||||
private Node createIndexedGraphic(Node graphic, int index) {
|
||||
final Label chip = new Label((index + 1) + ".");
|
||||
final Label chip = new Label(index + ".");
|
||||
chip.getStyleClass().add("studio-dual-list-index-chip");
|
||||
final HBox wrapper = new HBox(8, chip, graphic);
|
||||
wrapper.getStyleClass().add("studio-dual-list-indexed-cell");
|
||||
|
||||
@ -73,7 +73,7 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
|
||||
this.summaryControl = new AssetDetailsSummaryControl(projectReference, workspaceBus);
|
||||
this.contractControl = new AssetDetailsContractControl(projectReference, workspaceBus);
|
||||
this.bankCompositionControl = new AssetDetailsBankCompositionControl(projectReference, workspaceBus);
|
||||
this.paletteOverhaulingControl = new AssetDetailsPaletteOverhaulingControl(workspaceBus);
|
||||
this.paletteOverhaulingControl = new AssetDetailsPaletteOverhaulingControl(projectReference, workspaceBus);
|
||||
this.actionsSection = createActionsSection();
|
||||
|
||||
getStyleClass().add("assets-workspace-pane");
|
||||
@ -556,6 +556,7 @@ public final class AssetDetailsControl extends VBox implements StudioEventAware
|
||||
details.codecConfigurationFieldsByCodec(),
|
||||
details.metadataFields(),
|
||||
mapBankComposition(details.bankComposition()),
|
||||
details.pipelinePalettes(),
|
||||
mergedDiagnostics);
|
||||
}
|
||||
|
||||
|
||||
@ -1,23 +1,33 @@
|
||||
package p.studio.workspaces.assets.details.palette;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
import p.packer.events.PackerEventKind;
|
||||
import p.packer.messages.ApplyPaletteOverhaulingRequest;
|
||||
import p.studio.Container;
|
||||
import p.studio.controls.forms.StudioFormEditScopeChangedEvent;
|
||||
import p.studio.controls.forms.StudioFormMode;
|
||||
import p.studio.controls.forms.StudioFormSection;
|
||||
import p.studio.events.StudioPackerOperationEvent;
|
||||
import p.studio.events.StudioWorkspaceEventBus;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.utilities.i18n.I18n;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceDetailsViewState;
|
||||
import p.studio.workspaces.assets.messages.events.StudioAssetLogEvent;
|
||||
import p.studio.workspaces.assets.messages.events.StudioAssetsRefreshRequestedEvent;
|
||||
import p.studio.workspaces.assets.messages.events.StudioAssetsDetailsViewStateChangedEvent;
|
||||
import p.studio.workspaces.framework.StudioSubscriptionBag;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class AssetDetailsPaletteOverhaulingControl extends StudioFormSection {
|
||||
private static final String SECTION_ID = "asset-details.palette-overhauling";
|
||||
|
||||
private final ProjectReference projectReference;
|
||||
private final StudioWorkspaceEventBus workspaceBus;
|
||||
private final StudioSubscriptionBag subscriptions = new StudioSubscriptionBag();
|
||||
private final AssetDetailsPaletteOverhaulingCoordinator coordinator = new AssetDetailsPaletteOverhaulingCoordinator();
|
||||
@ -28,7 +38,8 @@ public final class AssetDetailsPaletteOverhaulingControl extends StudioFormSecti
|
||||
|
||||
private AssetWorkspaceDetailsViewState viewState;
|
||||
|
||||
public AssetDetailsPaletteOverhaulingControl(StudioWorkspaceEventBus workspaceBus) {
|
||||
public AssetDetailsPaletteOverhaulingControl(ProjectReference projectReference, StudioWorkspaceEventBus workspaceBus) {
|
||||
this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
|
||||
this.workspaceBus = Objects.requireNonNull(workspaceBus, "workspaceBus");
|
||||
body.getStyleClass().add("assets-details-palette-overhauling-body");
|
||||
content.setFillWidth(true);
|
||||
@ -149,9 +160,49 @@ public final class AssetDetailsPaletteOverhaulingControl extends StudioFormSecti
|
||||
|
||||
@Override
|
||||
protected void apply() {
|
||||
coordinator.apply();
|
||||
publishEditScope(workspaceBus, currentScopeKey(), null);
|
||||
renderSection();
|
||||
if (viewState == null || viewState.selectedAssetReference() == null || !coordinator.ready()) {
|
||||
return;
|
||||
}
|
||||
final var selectedPalettes = coordinator.viewModel().selectedFiles().stream()
|
||||
.map(file -> palettePayload(file.metadata()))
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
if (selectedPalettes.isEmpty()) {
|
||||
workspaceBus.publish(new StudioAssetLogEvent("palette-overhauling", "Apply failed: no palette was selected."));
|
||||
Container.eventBus().publish(new StudioPackerOperationEvent(
|
||||
UUID.randomUUID().toString(),
|
||||
PackerEventKind.ACTION_FAILED,
|
||||
"Palette overhauling apply failed: no palette was selected.",
|
||||
null,
|
||||
true));
|
||||
return;
|
||||
}
|
||||
|
||||
final var assetReference = viewState.selectedAssetReference();
|
||||
Container.backgroundTasks().submit(() -> {
|
||||
final var request = new ApplyPaletteOverhaulingRequest(
|
||||
projectReference.toPackerProjectContext(),
|
||||
assetReference,
|
||||
selectedPalettes);
|
||||
try {
|
||||
final var response = Container.packer().workspaceService().applyPaletteOverhauling(request);
|
||||
Platform.runLater(() -> {
|
||||
if (response.success()) {
|
||||
coordinator.apply();
|
||||
publishEditScope(workspaceBus, currentScopeKey(), null);
|
||||
renderSection();
|
||||
workspaceBus.publish(new StudioAssetsRefreshRequestedEvent(assetReference));
|
||||
} else {
|
||||
workspaceBus.publish(new StudioAssetLogEvent("palette-overhauling",
|
||||
"Apply failed: " + Objects.requireNonNullElse(response.errorMessage(), "unknown error")));
|
||||
}
|
||||
});
|
||||
} catch (Exception exception) {
|
||||
Platform.runLater(() -> workspaceBus.publish(new StudioAssetLogEvent(
|
||||
"palette-overhauling",
|
||||
"Apply failed: " + Objects.requireNonNullElse(exception.getMessage(), "unknown error"))));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -180,4 +231,10 @@ public final class AssetDetailsPaletteOverhaulingControl extends StudioFormSecti
|
||||
? null
|
||||
: "asset-details:" + viewState.selectedAssetReference();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> palettePayload(Map<String, Object> metadata) {
|
||||
final Object palette = metadata.get("palette");
|
||||
return palette instanceof Map<?, ?> paletteMap ? (Map<String, Object>) paletteMap : null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,13 +27,7 @@ public final class AssetDetailsPaletteOverhaulingCoordinator {
|
||||
final List<AssetWorkspaceBankCompositionFile> previewTileFiles = details.bankComposition().selectedFiles().stream()
|
||||
.filter(AssetDetailsPaletteOverhaulingCoordinator::supportsPalettePreview)
|
||||
.toList();
|
||||
final Map<String, AssetWorkspaceBankCompositionFile> filesByPath = allFiles.stream()
|
||||
.collect(java.util.stream.Collectors.toMap(
|
||||
AssetWorkspaceBankCompositionFile::path,
|
||||
file -> file,
|
||||
(left, right) -> left,
|
||||
LinkedHashMap::new));
|
||||
final List<AssetWorkspaceBankCompositionFile> initialSelected = new ArrayList<>();
|
||||
final List<AssetWorkspaceBankCompositionFile> initialSelected = initialSelectedPaletteFiles(details, allFiles);
|
||||
|
||||
final List<AssetWorkspaceBankCompositionFile> available = new ArrayList<>(allFiles);
|
||||
available.removeAll(initialSelected);
|
||||
@ -169,6 +163,29 @@ public final class AssetDetailsPaletteOverhaulingCoordinator {
|
||||
metadata);
|
||||
}
|
||||
|
||||
private static List<AssetWorkspaceBankCompositionFile> initialSelectedPaletteFiles(
|
||||
AssetWorkspaceAssetDetails details,
|
||||
List<AssetWorkspaceBankCompositionFile> allFiles) {
|
||||
final List<AssetWorkspaceBankCompositionFile> selected = new ArrayList<>();
|
||||
for (Map<String, Object> palette : details.pipelinePalettes()) {
|
||||
for (AssetWorkspaceBankCompositionFile file : allFiles) {
|
||||
if (!selected.contains(file) && paletteEquals(palette, nestedMap(file.metadata(), "palette"))) {
|
||||
selected.add(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return List.copyOf(selected);
|
||||
}
|
||||
|
||||
private static boolean paletteEquals(Map<String, Object> left, Map<String, Object> right) {
|
||||
if (left == null || right == null) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(left.get("originalArgb8888"), right.get("originalArgb8888"))
|
||||
&& Objects.equals(left.get("convertedRgb565"), right.get("convertedRgb565"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static String paletteHash(AssetWorkspaceBankCompositionFile file) {
|
||||
final Map<String, Object> palette = nestedMap(file.metadata(), "palette");
|
||||
|
||||
@ -18,6 +18,7 @@ public record AssetWorkspaceAssetDetails(
|
||||
Map<OutputCodecCatalog, List<PackerCodecConfigurationFieldDTO>> codecConfigurationFieldsByCodec,
|
||||
List<PackerCodecConfigurationFieldDTO> metadataFields,
|
||||
AssetWorkspaceBankCompositionDetails bankComposition,
|
||||
List<Map<String, Object>> pipelinePalettes,
|
||||
List<PackerDiagnosticDTO> diagnostics) {
|
||||
|
||||
public AssetWorkspaceAssetDetails {
|
||||
@ -29,6 +30,7 @@ public record AssetWorkspaceAssetDetails(
|
||||
codecConfigurationFieldsByCodec = Map.copyOf(Objects.requireNonNull(codecConfigurationFieldsByCodec, "codecConfigurationFieldsByCodec"));
|
||||
metadataFields = List.copyOf(Objects.requireNonNull(metadataFields, "metadataFields"));
|
||||
bankComposition = Objects.requireNonNull(bankComposition, "bankComposition");
|
||||
pipelinePalettes = List.copyOf(Objects.requireNonNull(pipelinePalettes, "pipelinePalettes"));
|
||||
diagnostics = List.copyOf(Objects.requireNonNull(diagnostics, "diagnostics"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,6 +112,7 @@ final class AssetDetailsBankCompositionCoordinatorTest {
|
||||
files("tile", fileCount, 1024L),
|
||||
List.of(),
|
||||
0L),
|
||||
List.of(),
|
||||
List.of());
|
||||
}
|
||||
|
||||
@ -130,6 +131,7 @@ final class AssetDetailsBankCompositionCoordinatorTest {
|
||||
new AssetWorkspaceBankCompositionFile("b.wav", "b.wav", secondSize, 1L, null, Map.of())),
|
||||
List.of(),
|
||||
0L),
|
||||
List.of(),
|
||||
List.of());
|
||||
}
|
||||
|
||||
|
||||
@ -91,6 +91,7 @@ final class AssetDetailsPaletteOverhaulingCoordinatorTest {
|
||||
Map.of(OutputCodecCatalog.NONE, List.of()),
|
||||
List.of(),
|
||||
new AssetWorkspaceBankCompositionDetails(availableFiles, selectedFiles, 0L),
|
||||
selectedFiles.stream().map(file -> (Map<String, Object>) file.metadata().get("palette")).toList(),
|
||||
List.of());
|
||||
}
|
||||
|
||||
|
||||
@ -198,6 +198,206 @@
|
||||
"message" : "Asset scan started",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bbb2",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: Bigode",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan started",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bbb2",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: Bigode",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan started",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bbb2",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: Bigode",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan started",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bbb2",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: Bigode",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan started",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Excluded asset in build: ui/sound",
|
||||
@ -2298,204 +2498,4 @@
|
||||
"message" : "Included asset in build: bigode",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bbb2",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: Bigode",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan started",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Excluded asset in build: bigode",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bbb2",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: Bigode",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan started",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Included asset in build: bigode",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bbb2",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: Bigode",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan started",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Excluded asset in build: bigode",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "7 assets loaded",
|
||||
"severity" : "SUCCESS",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Asset scan diagnostics updated.",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bla",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: ui_atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: one-more-atlas",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
}, {
|
||||
"source" : "Assets",
|
||||
"message" : "Discovered asset: bbb2",
|
||||
"severity" : "INFO",
|
||||
"sticky" : false
|
||||
} ]
|
||||
@ -9,6 +9,15 @@
|
||||
"codec_configuration" : { },
|
||||
"metadata" : {
|
||||
"tile_size" : "32x32"
|
||||
},
|
||||
"pipeline" : {
|
||||
"palettes" : [ {
|
||||
"originalArgb8888" : [ -265674, -1736296, -13905598, -11518505, -14439577, -2467509 ],
|
||||
"convertedRgb565" : [ 65414, 58387, 11912, 20986, 9548, 56009 ]
|
||||
}, {
|
||||
"originalArgb8888" : [ -265674 ],
|
||||
"convertedRgb565" : [ 65414 ]
|
||||
} ]
|
||||
}
|
||||
},
|
||||
"preload" : {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user