CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-net-kyori--adventure-api

A serverside user interface library for Minecraft: Java Edition

Pending
Overview
Eval results
Files

resource-packs.mddocs/

Resource Packs

Resource pack management system for sending, tracking, and handling resource pack requests with callback support. Adventure provides comprehensive resource pack management for custom content delivery.

Capabilities

Resource Pack Request

Interface for creating and managing resource pack requests sent to audiences.

/**
 * Request to send resource pack to audience
 */
interface ResourcePackRequest extends Examinable {
    /**
     * Gets the resource pack information
     * @return the resource pack info
     */
    ResourcePackInfo packs();
    
    /**
     * Checks if this request replaces existing packs
     * @return true if should replace existing packs
     */
    boolean replace();
    
    /**
     * Gets the optional prompt message
     * @return the prompt or null
     */
    @Nullable Component prompt();
    
    /**
     * Gets the callback for pack events
     * @return the callback or null
     */
    @Nullable ResourcePackCallback callback();
    
    /**
     * Creates a resource pack request
     * @param pack the resource pack info
     * @return new request
     */
    static ResourcePackRequest resourcePackRequest(ResourcePackInfo pack);
    
    /**
     * Creates a builder for resource pack requests
     * @return new builder
     */
    static Builder resourcePackRequest();
    
    interface Builder extends AbstractBuilder<ResourcePackRequest> {
        Builder packs(ResourcePackInfo packs);
        Builder replace(boolean replace);
        Builder prompt(@Nullable ComponentLike prompt);
        Builder callback(@Nullable ResourcePackCallback callback);
    }
}

Resource Pack Info

Information about a resource pack including ID, URL, and hash for verification.

/**
 * Information about a resource pack
 */
interface ResourcePackInfo extends Examinable {
    /**
     * Gets the unique resource pack ID
     * @return the pack ID
     */
    String id();
    
    /**
     * Gets the download URL
     * @return the pack URL
     */
    String uri();
    
    /**
     * Gets the SHA-1 hash for verification
     * @return the hash string
     */
    String hash();
    
    /**
     * Creates resource pack info
     * @param id the pack ID
     * @param uri the download URL
     * @param hash the SHA-1 hash
     * @return new resource pack info
     */
    static ResourcePackInfo resourcePackInfo(String id, String uri, String hash);
    
    /**
     * Creates a builder
     * @return new builder
     */
    static Builder builder();
    
    interface Builder extends AbstractBuilder<ResourcePackInfo> {
        Builder id(String id);
        Builder uri(String uri);
        Builder hash(String hash);
    }
}

Resource Pack Status

Enumeration of possible resource pack status values reported by clients.

/**
 * Status of resource pack download/application
 */
enum ResourcePackStatus {
    /**
     * Resource pack loaded successfully
     */
    SUCCESSFULLY_LOADED("successfully_loaded"),
    
    /**
     * Player declined the resource pack
     */
    DECLINED("declined"),
    
    /**
     * Failed to download the resource pack
     */
    FAILED_DOWNLOAD("failed_download"),
    
    /**
     * Player accepted the resource pack prompt
     */
    ACCEPTED("accepted"),
    
    /**
     * Resource pack downloaded successfully
     */
    DOWNLOADED("downloaded"),
    
    /**
     * Invalid URL provided
     */
    INVALID_URL("invalid_url"),
    
    /**
     * Failed to reload resource pack
     */
    FAILED_TO_RELOAD("failed_to_reload"),
    
    /**
     * Resource pack was discarded
     */
    DISCARDED("discarded");
}

Resource Pack Callback

Callback interface for handling resource pack events and status updates.

/**
 * Callback interface for resource pack events
 */
interface ResourcePackCallback {
    /**
     * Called when a resource pack event occurs
     * @param id the resource pack ID
     * @param status the pack status
     * @param audience the audience that triggered the event
     */
    void packEventReceived(String id, ResourcePackStatus status, Audience audience);
    
    /**
     * Creates a simple callback
     * @param callback the callback function
     * @return new resource pack callback
     */
    static ResourcePackCallback callback(ResourcePackEventConsumer callback);
    
    /**
     * Functional interface for resource pack events
     */
    @FunctionalInterface
    interface ResourcePackEventConsumer {
        void accept(String id, ResourcePackStatus status, Audience audience);
    }
}

Usage Examples:

import net.kyori.adventure.resource.*;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;

// Basic resource pack request
ResourcePackInfo pack = ResourcePackInfo.resourcePackInfo(
    "my-pack-v1.0",
    "https://example.com/packs/mypack.zip", 
    "abc123def456..." // SHA-1 hash
);

ResourcePackRequest request = ResourcePackRequest.resourcePackRequest(pack);
audience.sendResourcePacks(request);

// Resource pack with prompt and callback
ResourcePackRequest customRequest = ResourcePackRequest.resourcePackRequest()
    .packs(pack)
    .replace(true)  // Replace existing packs
    .prompt(Component.text("Download our custom textures?", NamedTextColor.YELLOW))
    .callback((id, status, audience) -> {
        switch (status) {
            case SUCCESSFULLY_LOADED:
                audience.sendMessage(Component.text("Resource pack loaded!", NamedTextColor.GREEN));
                break;
            case DECLINED:
                audience.sendMessage(Component.text("Resource pack declined", NamedTextColor.RED));
                break;
            case FAILED_DOWNLOAD:
                audience.sendMessage(Component.text("Failed to download resource pack", NamedTextColor.RED));
                break;
        }
    })
    .build();

audience.sendResourcePacks(customRequest);

// Remove resource packs
audience.removeResourcePacks(UUID.fromString("pack-uuid"));
audience.clearResourcePacks(); // Remove all packs

Resource Pack Management Patterns

Pack Version Management

public class ResourcePackManager {
    private final Map<String, ResourcePackInfo> packs = new HashMap<>();
    
    public void registerPack(String name, String version, String url, String hash) {
        String packId = name + "-" + version;
        ResourcePackInfo pack = ResourcePackInfo.resourcePackInfo(packId, url, hash);
        packs.put(name, pack);
    }
    
    public void sendLatestPack(Audience audience, String packName) {
        ResourcePackInfo pack = packs.get(packName);
        if (pack != null) {
            ResourcePackRequest request = ResourcePackRequest.resourcePackRequest()
                .packs(pack)
                .replace(true)
                .prompt(Component.text("Install " + packName + "?"))
                .callback(createCallback(packName))
                .build();
            
            audience.sendResourcePacks(request);
        }
    }
    
    private ResourcePackCallback createCallback(String packName) {
        return (id, status, audience) -> {
            logPackEvent(packName, id, status, audience);
            handlePackStatus(packName, status, audience);
        };
    }
    
    private void handlePackStatus(String packName, ResourcePackStatus status, Audience audience) {
        switch (status) {
            case SUCCESSFULLY_LOADED:
                onPackLoaded(audience, packName);
                break;
            case DECLINED:
                onPackDeclined(audience, packName);
                break;
            case FAILED_DOWNLOAD:
            case INVALID_URL:
                onPackFailed(audience, packName);
                break;
        }
    }
}

Conditional Pack Deployment

public class ConditionalPacks {
    public void sendPackBasedOnVersion(Audience audience, String clientVersion) {
        ResourcePackInfo pack;
        
        if (isVersion(clientVersion, "1.20")) {
            pack = getPackForVersion("1.20");
        } else if (isVersion(clientVersion, "1.19")) {
            pack = getPackForVersion("1.19");  
        } else {
            pack = getLegacyPack();
        }
        
        if (pack != null) {
            sendPackWithVersionInfo(audience, pack, clientVersion);
        }
    }
    
    public void sendOptionalPacks(Audience audience, Set<String> playerPreferences) {
        List<ResourcePackInfo> packsToSend = new ArrayList<>();
        
        if (playerPreferences.contains("hd-textures")) {
            packsToSend.add(getHDTexturePack());
        }
        
        if (playerPreferences.contains("custom-sounds")) {
            packsToSend.add(getCustomSoundPack());
        }
        
        if (playerPreferences.contains("ui-improvements")) {
            packsToSend.add(getUIImprovementPack());
        }
        
        // Send packs sequentially or as a bundle
        sendPackBundle(audience, packsToSend);
    }
}

Pack Status Tracking

public class PackStatusTracker {
    private final Map<String, Map<String, ResourcePackStatus>> playerPackStatus = new HashMap<>();
    private final Set<ResourcePackStatusListener> listeners = new HashSet<>();
    
    public ResourcePackCallback createTrackingCallback(String playerId) {
        return (id, status, audience) -> {
            updatePlayerPackStatus(playerId, id, status);
            notifyListeners(playerId, id, status);
            
            // Auto-retry on failure
            if (status == ResourcePackStatus.FAILED_DOWNLOAD) {
                scheduleRetry(audience, id);
            }
        };
    }
    
    private void updatePlayerPackStatus(String playerId, String packId, ResourcePackStatus status) {
        playerPackStatus.computeIfAbsent(playerId, k -> new HashMap<>())
                        .put(packId, status);
    }
    
    public boolean hasPlayerLoadedPack(String playerId, String packId) {
        return playerPackStatus.getOrDefault(playerId, Collections.emptyMap())
                               .get(packId) == ResourcePackStatus.SUCCESSFULLY_LOADED;
    }
    
    public Set<String> getLoadedPacks(String playerId) {
        return playerPackStatus.getOrDefault(playerId, Collections.emptyMap())
                               .entrySet().stream()
                               .filter(entry -> entry.getValue() == ResourcePackStatus.SUCCESSFULLY_LOADED)
                               .map(Map.Entry::getKey)
                               .collect(Collectors.toSet());
    }
    
    private void scheduleRetry(Audience audience, String packId) {
        // Implementation-specific retry logic
        scheduleTask(() -> {
            ResourcePackInfo pack = getPackById(packId);
            if (pack != null) {
                ResourcePackRequest retry = ResourcePackRequest.resourcePackRequest()
                    .packs(pack)
                    .prompt(Component.text("Retrying resource pack download..."))
                    .callback(createTrackingCallback(getPlayerId(audience)))
                    .build();
                audience.sendResourcePacks(retry);
            }
        }, 5000L); // Retry after 5 seconds  
    }
}

Pack Validation and Security

public class PackValidator {
    private static final int MAX_PACK_SIZE = 50 * 1024 * 1024; // 50MB
    private static final Set<String> ALLOWED_DOMAINS = Set.of(
        "example.com",
        "cdn.example.com",
        "packs.myserver.net"
    );
    
    public boolean validatePackInfo(ResourcePackInfo pack) {
        // Validate URL
        if (!isValidUrl(pack.uri())) {
            return false;
        }
        
        // Validate hash format
        if (!isValidSHA1Hash(pack.hash())) {
            return false;
        }
        
        // Check domain whitelist
        if (!isAllowedDomain(pack.uri())) {
            return false;
        }
        
        return true;
    }
    
    private boolean isValidUrl(String url) {
        try {
            URL parsedUrl = new URL(url);
            return "https".equals(parsedUrl.getProtocol()) || 
                   "http".equals(parsedUrl.getProtocol());
        } catch (MalformedURLException e) {
            return false;
        }
    }
    
    private boolean isValidSHA1Hash(String hash) {
        return hash != null && 
               hash.length() == 40 && 
               hash.matches("[a-fA-F0-9]+");
    }
    
    private boolean isAllowedDomain(String url) {
        try {
            String host = new URL(url).getHost();
            return ALLOWED_DOMAINS.contains(host);
        } catch (MalformedURLException e) {
            return false;
        }
    }
    
    public ResourcePackRequest createSecureRequest(ResourcePackInfo pack) {
        if (!validatePackInfo(pack)) {
            throw new IllegalArgumentException("Invalid resource pack info");
        }
        
        return ResourcePackRequest.resourcePackRequest()
            .packs(pack)
            .callback(createSecurityCallback())
            .build();
    }
    
    private ResourcePackCallback createSecurityCallback() {
        return (id, status, audience) -> {
            if (status == ResourcePackStatus.INVALID_URL) {
                logger.warn("Invalid URL detected for pack: {}", id);
            } else if (status == ResourcePackStatus.FAILED_DOWNLOAD) {
                logger.warn("Download failed for pack: {}", id);
            }
        };
    }
}

Best Practices

Resource Pack Design

  • Keep pack sizes reasonable (< 50MB recommended)
  • Use appropriate compression for textures and sounds
  • Test packs across different client versions
  • Provide fallbacks for optional content

URL and Security

  • Use HTTPS URLs for security
  • Implement domain whitelisting for pack sources
  • Validate SHA-1 hashes to ensure integrity
  • Monitor pack download success rates

User Experience

  • Provide clear prompts explaining what the pack contains
  • Handle pack failures gracefully with helpful messages
  • Allow players to opt-out of optional packs
  • Provide pack management commands for players

Performance

  • Cache pack information to avoid repeated requests
  • Implement retry logic for failed downloads
  • Monitor pack loading times and success rates
  • Use CDNs for pack distribution when possible

Install with Tessl CLI

npx tessl i tessl/maven-net-kyori--adventure-api

docs

audience-system.md

books-and-inventory.md

boss-bars.md

events-and-interactivity.md

index.md

nbt-data-components.md

resource-packs.md

sound-system.md

text-components.md

text-formatting.md

titles-and-subtitles.md

translation-system.md

tile.json