mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-12-22 22:45:04 +01:00
Exposing resourcepack loading to api (#3696)
Co-authored-by: Konicai <71294714+Konicai@users.noreply.github.com> Co-authored-by: RednedEpic <redned235@gmail.com>
This commit is contained in:
parent
6eca6ade06
commit
903e61f1a3
27 changed files with 1334 additions and 347 deletions
|
@ -36,6 +36,7 @@ import org.geysermc.geyser.api.extension.ExtensionManager;
|
|||
import org.geysermc.geyser.api.network.BedrockListener;
|
||||
import org.geysermc.geyser.api.network.RemoteServer;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -107,6 +108,22 @@ public interface GeyserApi extends GeyserApiBase {
|
|||
@NonNull
|
||||
BedrockListener bedrockListener();
|
||||
|
||||
/**
|
||||
* Gets the {@link Path} to the Geyser config directory.
|
||||
*
|
||||
* @return the path to the Geyser config directory
|
||||
*/
|
||||
@NonNull
|
||||
Path configDirectory();
|
||||
|
||||
/**
|
||||
* Gets the {@link Path} to the Geyser packs directory.
|
||||
*
|
||||
* @return the path to the Geyser packs directory
|
||||
*/
|
||||
@NonNull
|
||||
Path packDirectory();
|
||||
|
||||
/**
|
||||
* Gets the current {@link GeyserApiBase} instance.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.event.bedrock;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Called when Geyser initializes a session for a new Bedrock client and is in the process of sending resource packs.
|
||||
*/
|
||||
public abstract class SessionLoadResourcePacksEvent extends ConnectionEvent {
|
||||
public SessionLoadResourcePacksEvent(@NonNull GeyserConnection connection) {
|
||||
super(connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an unmodifiable list of {@link ResourcePack}s that will be sent to the client.
|
||||
*
|
||||
* @return an unmodifiable list of resource packs that will be sent to the client.
|
||||
*/
|
||||
public abstract @NonNull List<ResourcePack> resourcePacks();
|
||||
|
||||
/**
|
||||
* Registers a {@link ResourcePack} to be sent to the client.
|
||||
*
|
||||
* @param resourcePack a resource pack that will be sent to the client.
|
||||
* @return true if the resource pack was added successfully,
|
||||
* or false if already present
|
||||
*/
|
||||
public abstract boolean register(@NonNull ResourcePack resourcePack);
|
||||
|
||||
/**
|
||||
* Unregisters a resource pack from being sent to the client.
|
||||
*
|
||||
* @param uuid the UUID of the resource pack
|
||||
* @return true whether the resource pack was removed from the list of resource packs.
|
||||
*/
|
||||
public abstract boolean unregister(@NonNull UUID uuid);
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.pack;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Represents a pack codec that can be used
|
||||
* to provide resource packs to clients.
|
||||
*/
|
||||
public abstract class PackCodec {
|
||||
|
||||
/**
|
||||
* Gets the sha256 hash of the resource pack.
|
||||
*
|
||||
* @return the hash of the resource pack
|
||||
*/
|
||||
public abstract byte @NonNull [] sha256();
|
||||
|
||||
/**
|
||||
* Gets the resource pack size.
|
||||
*
|
||||
* @return the resource pack file size
|
||||
*/
|
||||
public abstract long size();
|
||||
|
||||
/**
|
||||
* Serializes the given resource pack into a byte buffer.
|
||||
*
|
||||
* @param resourcePack the resource pack to serialize
|
||||
* @return the serialized resource pack
|
||||
*/
|
||||
@NonNull
|
||||
public abstract SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws IOException;
|
||||
|
||||
/**
|
||||
* Creates a new resource pack from this codec.
|
||||
*
|
||||
* @return the new resource pack
|
||||
*/
|
||||
@NonNull
|
||||
protected abstract ResourcePack create();
|
||||
|
||||
/**
|
||||
* Creates a new pack provider from the given path.
|
||||
*
|
||||
* @param path the path to create the pack provider from
|
||||
* @return the new pack provider
|
||||
*/
|
||||
@NonNull
|
||||
public static PackCodec path(@NonNull Path path) {
|
||||
return GeyserApi.api().provider(PathPackCodec.class, path);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.pack;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Represents a pack codec that creates a resource
|
||||
* pack from a path on the filesystem.
|
||||
*/
|
||||
public abstract class PathPackCodec extends PackCodec {
|
||||
|
||||
/**
|
||||
* Gets the path of the resource pack.
|
||||
*
|
||||
* @return the path of the resource pack
|
||||
*/
|
||||
@NonNull
|
||||
public abstract Path path();
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.pack;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* Represents a resource pack sent to Bedrock clients
|
||||
* <p>
|
||||
* This representation of a resource pack only contains what
|
||||
* Geyser requires to send it to the client.
|
||||
*/
|
||||
public interface ResourcePack {
|
||||
|
||||
/**
|
||||
* The {@link PackCodec codec} for this pack.
|
||||
*
|
||||
* @return the codec for this pack
|
||||
*/
|
||||
@NonNull
|
||||
PackCodec codec();
|
||||
|
||||
/**
|
||||
* Gets the resource pack manifest.
|
||||
*
|
||||
* @return the resource pack manifest
|
||||
*/
|
||||
@NonNull
|
||||
ResourcePackManifest manifest();
|
||||
|
||||
/**
|
||||
* Gets the content key of the resource pack. Lack of a content key is represented by an empty String.
|
||||
*
|
||||
* @return the content key of the resource pack
|
||||
*/
|
||||
@NonNull
|
||||
String contentKey();
|
||||
|
||||
/**
|
||||
* Creates a resource pack with the given {@link PackCodec}.
|
||||
*
|
||||
* @param codec the pack codec
|
||||
* @return the resource pack
|
||||
*/
|
||||
@NonNull
|
||||
static ResourcePack create(@NonNull PackCodec codec) {
|
||||
return codec.create();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.pack;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a resource pack manifest.
|
||||
*/
|
||||
public interface ResourcePackManifest {
|
||||
|
||||
/**
|
||||
* Gets the format version of the resource pack.
|
||||
*
|
||||
* @return the format version
|
||||
*/
|
||||
int formatVersion();
|
||||
|
||||
/**
|
||||
* Gets the header of the resource pack.
|
||||
*
|
||||
* @return the header
|
||||
*/
|
||||
@NonNull
|
||||
Header header();
|
||||
|
||||
/**
|
||||
* Gets the modules of the resource pack.
|
||||
*
|
||||
* @return the modules
|
||||
*/
|
||||
@NonNull
|
||||
Collection<? extends Module> modules();
|
||||
|
||||
/**
|
||||
* Gets the dependencies of the resource pack.
|
||||
*
|
||||
* @return the dependencies
|
||||
*/
|
||||
@NonNull
|
||||
Collection<? extends Dependency> dependencies();
|
||||
|
||||
/**
|
||||
* Represents the header of a resource pack.
|
||||
*/
|
||||
interface Header {
|
||||
|
||||
/**
|
||||
* Gets the UUID of the resource pack.
|
||||
*
|
||||
* @return the UUID
|
||||
*/
|
||||
@NonNull
|
||||
UUID uuid();
|
||||
|
||||
/**
|
||||
* Gets the version of the resource pack.
|
||||
*
|
||||
* @return the version
|
||||
*/
|
||||
@NonNull
|
||||
Version version();
|
||||
|
||||
/**
|
||||
* Gets the name of the resource pack.
|
||||
*
|
||||
* @return the name
|
||||
*/
|
||||
@NonNull
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Gets the description of the resource pack.
|
||||
*
|
||||
* @return the description
|
||||
*/
|
||||
@NonNull
|
||||
String description();
|
||||
|
||||
/**
|
||||
* Gets the minimum supported Minecraft version of the resource pack.
|
||||
*
|
||||
* @return the minimum supported Minecraft version
|
||||
*/
|
||||
@NonNull
|
||||
Version minimumSupportedMinecraftVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a module of a resource pack.
|
||||
*/
|
||||
interface Module {
|
||||
|
||||
/**
|
||||
* Gets the UUID of the module.
|
||||
*
|
||||
* @return the UUID
|
||||
*/
|
||||
@NonNull
|
||||
UUID uuid();
|
||||
|
||||
/**
|
||||
* Gets the version of the module.
|
||||
*
|
||||
* @return the version
|
||||
*/
|
||||
@NonNull
|
||||
Version version();
|
||||
|
||||
/**
|
||||
* Gets the type of the module.
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
@NonNull
|
||||
String type();
|
||||
|
||||
/**
|
||||
* Gets the description of the module.
|
||||
*
|
||||
* @return the description
|
||||
*/
|
||||
@NonNull
|
||||
String description();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a dependency of a resource pack.
|
||||
*/
|
||||
interface Dependency {
|
||||
|
||||
/**
|
||||
* Gets the UUID of the dependency.
|
||||
*
|
||||
* @return the uuid
|
||||
*/
|
||||
@NonNull
|
||||
UUID uuid();
|
||||
|
||||
/**
|
||||
* Gets the version of the dependency.
|
||||
*
|
||||
* @return the version
|
||||
*/
|
||||
@NonNull
|
||||
Version version();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a version of a resource pack.
|
||||
*/
|
||||
interface Version {
|
||||
|
||||
/**
|
||||
* Gets the major version.
|
||||
*
|
||||
* @return the major version
|
||||
*/
|
||||
int major();
|
||||
|
||||
/**
|
||||
* Gets the minor version.
|
||||
*
|
||||
* @return the minor version
|
||||
*/
|
||||
int minor();
|
||||
|
||||
/**
|
||||
* Gets the patch version.
|
||||
*
|
||||
* @return the patch version
|
||||
*/
|
||||
int patch();
|
||||
|
||||
/**
|
||||
* Gets the version formatted as a String.
|
||||
*
|
||||
* @return the version string
|
||||
*/
|
||||
@NonNull String toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@ dependencies {
|
|||
}
|
||||
|
||||
implementation(libs.raknet) {
|
||||
exclude("io.netty", "*");
|
||||
exclude("io.netty", "*")
|
||||
}
|
||||
|
||||
implementation(libs.netty.resolver.dns)
|
||||
|
|
|
@ -69,9 +69,9 @@ import org.geysermc.geyser.event.GeyserEventBus;
|
|||
import org.geysermc.geyser.extension.GeyserExtensionManager;
|
||||
import org.geysermc.geyser.level.WorldManager;
|
||||
import org.geysermc.geyser.network.netty.GeyserServer;
|
||||
import org.geysermc.geyser.pack.ResourcePack;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.registry.loader.RegistryLoaders;
|
||||
import org.geysermc.geyser.scoreboard.ScoreboardUpdater;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
|
||||
|
@ -90,6 +90,7 @@ import java.io.IOException;
|
|||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.Key;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.*;
|
||||
|
@ -258,7 +259,7 @@ public class GeyserImpl implements GeyserApi {
|
|||
|
||||
SkinProvider.registerCacheImageTask(this);
|
||||
|
||||
ResourcePack.loadPacks();
|
||||
Registries.RESOURCE_PACKS.load();
|
||||
|
||||
String geyserUdpPort = System.getProperty("geyserUdpPort", "");
|
||||
String pluginUdpPort = geyserUdpPort.isEmpty() ? System.getProperty("pluginUdpPort", "") : geyserUdpPort;
|
||||
|
@ -622,7 +623,7 @@ public class GeyserImpl implements GeyserApi {
|
|||
this.erosionUnixListener.close();
|
||||
}
|
||||
|
||||
ResourcePack.PACKS.clear();
|
||||
Registries.RESOURCE_PACKS.get().clear();
|
||||
|
||||
this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.eventBus));
|
||||
this.extensionManager.disableExtensions();
|
||||
|
@ -681,6 +682,18 @@ public class GeyserImpl implements GeyserApi {
|
|||
return getConfig().getBedrock();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Path configDirectory() {
|
||||
return bootstrap.getConfigFolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Path packDirectory() {
|
||||
return bootstrap.getConfigFolder().resolve("packs");
|
||||
}
|
||||
|
||||
public int buildNumber() {
|
||||
if (!this.isProductionEnvironment()) {
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.event.type;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.event.bedrock.SessionLoadResourcePacksEvent;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksEvent {
|
||||
|
||||
private final Map<String, ResourcePack> packs;
|
||||
|
||||
public SessionLoadResourcePacksEventImpl(GeyserSession session, Map<String, ResourcePack> packMap) {
|
||||
super(session);
|
||||
this.packs = packMap;
|
||||
}
|
||||
|
||||
public @NonNull Map<String, ResourcePack> getPacks() {
|
||||
return packs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<ResourcePack> resourcePacks() {
|
||||
return List.copyOf(packs.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean register(@NonNull ResourcePack resourcePack) {
|
||||
String packID = resourcePack.manifest().header().uuid().toString();
|
||||
if (packs.containsValue(resourcePack) || packs.containsKey(packID)) {
|
||||
return false;
|
||||
}
|
||||
packs.put(resourcePack.manifest().header().uuid().toString(), resourcePack);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregister(@NonNull UUID uuid) {
|
||||
return packs.remove(uuid.toString()) != null;
|
||||
}
|
||||
}
|
|
@ -49,9 +49,12 @@ import org.cloudburstmc.protocol.common.PacketSignal;
|
|||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.network.AuthType;
|
||||
import org.geysermc.geyser.api.pack.PackCodec;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.api.pack.ResourcePackManifest;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.pack.ResourcePack;
|
||||
import org.geysermc.geyser.pack.ResourcePackManifest;
|
||||
import org.geysermc.geyser.event.type.SessionLoadResourcePacksEventImpl;
|
||||
import org.geysermc.geyser.pack.GeyserResourcePack;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
@ -61,16 +64,20 @@ import org.geysermc.geyser.util.LoginEncryptionUtils;
|
|||
import org.geysermc.geyser.util.MathUtils;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.OptionalInt;
|
||||
|
||||
public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
|
||||
private boolean networkSettingsRequested = false;
|
||||
private Deque<String> packsToSent = new ArrayDeque<>();
|
||||
private final Deque<String> packsToSent = new ArrayDeque<>();
|
||||
|
||||
private SessionLoadResourcePacksEventImpl resourcePackLoadEvent;
|
||||
|
||||
public UpstreamPacketHandler(GeyserImpl geyser, GeyserSession session) {
|
||||
super(geyser, session);
|
||||
|
@ -172,12 +179,16 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
|
||||
geyser.getSessionManager().addPendingSession(session);
|
||||
|
||||
this.resourcePackLoadEvent = new SessionLoadResourcePacksEventImpl(session, new HashMap<>(Registries.RESOURCE_PACKS.get()));
|
||||
this.geyser.eventBus().fire(this.resourcePackLoadEvent);
|
||||
|
||||
ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
|
||||
for(ResourcePack resourcePack : ResourcePack.PACKS.values()) {
|
||||
ResourcePackManifest.Header header = resourcePack.getManifest().getHeader();
|
||||
for (ResourcePack pack : this.resourcePackLoadEvent.resourcePacks()) {
|
||||
PackCodec codec = pack.codec();
|
||||
ResourcePackManifest.Header header = pack.manifest().header();
|
||||
resourcePacksInfo.getResourcePackInfos().add(new ResourcePacksInfoPacket.Entry(
|
||||
header.getUuid().toString(), header.getVersionString(), resourcePack.getFile().length(),
|
||||
resourcePack.getContentKey(), "", header.getUuid().toString(), false, false));
|
||||
header.uuid().toString(), header.version().toString(), codec.size(), pack.contentKey(),
|
||||
"", header.uuid().toString(), false, false));
|
||||
}
|
||||
resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().getConfig().isForceResourcePacks());
|
||||
session.sendUpstreamPacket(resourcePacksInfo);
|
||||
|
@ -210,9 +221,9 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
stackPacket.setForcedToAccept(false); // Leaving this as false allows the player to choose to download or not
|
||||
stackPacket.setGameVersion(session.getClientData().getGameVersion());
|
||||
|
||||
for (ResourcePack pack : ResourcePack.PACKS.values()) {
|
||||
ResourcePackManifest.Header header = pack.getManifest().getHeader();
|
||||
stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.getUuid().toString(), header.getVersionString(), ""));
|
||||
for (ResourcePack pack : this.resourcePackLoadEvent.resourcePacks()) {
|
||||
ResourcePackManifest.Header header = pack.manifest().header();
|
||||
stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.uuid().toString(), header.version().toString(), ""));
|
||||
}
|
||||
|
||||
if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) {
|
||||
|
@ -291,21 +302,22 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
@Override
|
||||
public PacketSignal handle(ResourcePackChunkRequestPacket packet) {
|
||||
ResourcePackChunkDataPacket data = new ResourcePackChunkDataPacket();
|
||||
ResourcePack pack = ResourcePack.PACKS.get(packet.getPackId().toString());
|
||||
ResourcePack pack = this.resourcePackLoadEvent.getPacks().get(packet.getPackId().toString());
|
||||
PackCodec codec = pack.codec();
|
||||
|
||||
data.setChunkIndex(packet.getChunkIndex());
|
||||
data.setProgress(packet.getChunkIndex() * ResourcePack.CHUNK_SIZE);
|
||||
data.setProgress((long) packet.getChunkIndex() * GeyserResourcePack.CHUNK_SIZE);
|
||||
data.setPackVersion(packet.getPackVersion());
|
||||
data.setPackId(packet.getPackId());
|
||||
|
||||
int offset = packet.getChunkIndex() * ResourcePack.CHUNK_SIZE;
|
||||
long remainingSize = pack.getFile().length() - offset;
|
||||
byte[] packData = new byte[(int) MathUtils.constrain(remainingSize, 0, ResourcePack.CHUNK_SIZE)];
|
||||
int offset = packet.getChunkIndex() * GeyserResourcePack.CHUNK_SIZE;
|
||||
long remainingSize = codec.size() - offset;
|
||||
byte[] packData = new byte[(int) MathUtils.constrain(remainingSize, 0, GeyserResourcePack.CHUNK_SIZE)];
|
||||
|
||||
try (InputStream inputStream = new FileInputStream(pack.getFile())) {
|
||||
inputStream.skip(offset);
|
||||
inputStream.read(packData, 0, packData.length);
|
||||
} catch (Exception e) {
|
||||
try (SeekableByteChannel channel = codec.serialize(pack)) {
|
||||
channel.position(offset);
|
||||
channel.read(ByteBuffer.wrap(packData, 0, packData.length));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
@ -314,7 +326,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
session.sendUpstreamPacket(data);
|
||||
|
||||
// Check if it is the last chunk and send next pack in queue when available.
|
||||
if (remainingSize <= ResourcePack.CHUNK_SIZE && !packsToSent.isEmpty()) {
|
||||
if (remainingSize <= GeyserResourcePack.CHUNK_SIZE && !packsToSent.isEmpty()) {
|
||||
sendPackDataInfo(packsToSent.pop());
|
||||
}
|
||||
|
||||
|
@ -324,15 +336,16 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
private void sendPackDataInfo(String id) {
|
||||
ResourcePackDataInfoPacket data = new ResourcePackDataInfoPacket();
|
||||
String[] packID = id.split("_");
|
||||
ResourcePack pack = ResourcePack.PACKS.get(packID[0]);
|
||||
ResourcePackManifest.Header header = pack.getManifest().getHeader();
|
||||
ResourcePack pack = this.resourcePackLoadEvent.getPacks().get(packID[0]);
|
||||
PackCodec codec = pack.codec();
|
||||
ResourcePackManifest.Header header = pack.manifest().header();
|
||||
|
||||
data.setPackId(header.getUuid());
|
||||
int chunkCount = (int) Math.ceil((int) pack.getFile().length() / (double) ResourcePack.CHUNK_SIZE);
|
||||
data.setPackId(header.uuid());
|
||||
int chunkCount = (int) Math.ceil(codec.size() / (double) GeyserResourcePack.CHUNK_SIZE);
|
||||
data.setChunkCount(chunkCount);
|
||||
data.setCompressedPackSize(pack.getFile().length());
|
||||
data.setMaxChunkSize(ResourcePack.CHUNK_SIZE);
|
||||
data.setHash(pack.getSha256());
|
||||
data.setCompressedPackSize(codec.size());
|
||||
data.setMaxChunkSize(GeyserResourcePack.CHUNK_SIZE);
|
||||
data.setHash(codec.sha256());
|
||||
data.setPackVersion(packID[1]);
|
||||
data.setPremium(false);
|
||||
data.setType(ResourcePackType.RESOURCES);
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.pack;
|
||||
|
||||
import org.geysermc.geyser.api.pack.PackCodec;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.api.pack.ResourcePackManifest;
|
||||
|
||||
public record GeyserResourcePack(PackCodec codec, ResourcePackManifest manifest, String contentKey) implements ResourcePack {
|
||||
|
||||
/**
|
||||
* The size of each chunk to use when sending the resource packs to clients in bytes
|
||||
*/
|
||||
public static final int CHUNK_SIZE = 102400;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.pack;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import org.geysermc.geyser.api.pack.ResourcePackManifest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
public record GeyserResourcePackManifest(@JsonProperty("format_version") int formatVersion, Header header, Collection<Module> modules, Collection<Dependency> dependencies) implements ResourcePackManifest {
|
||||
|
||||
public record Header(UUID uuid, Version version, String name, String description, @JsonProperty("min_engine_version") Version minimumSupportedMinecraftVersion) implements ResourcePackManifest.Header { }
|
||||
|
||||
public record Module(UUID uuid, Version version, String type, String description) implements ResourcePackManifest.Module { }
|
||||
|
||||
public record Dependency(UUID uuid, Version version) implements ResourcePackManifest.Dependency { }
|
||||
|
||||
@JsonDeserialize(using = Version.VersionDeserializer.class)
|
||||
public record Version(int major, int minor, int patch) implements ResourcePackManifest.Version {
|
||||
|
||||
@Override
|
||||
public @NotNull String toString() {
|
||||
return major + "." + minor + "." + patch;
|
||||
}
|
||||
|
||||
public static class VersionDeserializer extends JsonDeserializer<Version> {
|
||||
@Override
|
||||
public Version deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
int[] version = ctxt.readValue(p, int[].class);
|
||||
return new Version(version[0], version[1], version[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.pack;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserLoadResourcePacksEvent;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* This represents a resource pack and all the data relevant to it
|
||||
*/
|
||||
public class ResourcePack {
|
||||
/**
|
||||
* The list of loaded resource packs
|
||||
*/
|
||||
public static final Map<String, ResourcePack> PACKS = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The size of each chunk to use when sending the resource packs to clients in bytes
|
||||
*/
|
||||
public static final int CHUNK_SIZE = 102400;
|
||||
|
||||
private byte[] sha256;
|
||||
private File file;
|
||||
private ResourcePackManifest manifest;
|
||||
private ResourcePackManifest.Version version;
|
||||
|
||||
@Getter
|
||||
private String contentKey;
|
||||
|
||||
/**
|
||||
* Loop through the packs directory and locate valid resource pack files
|
||||
*/
|
||||
public static void loadPacks() {
|
||||
Path directory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("packs");
|
||||
|
||||
if (!Files.exists(directory)) {
|
||||
try {
|
||||
Files.createDirectory(directory);
|
||||
} catch (IOException e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Could not create packs directory", e);
|
||||
}
|
||||
|
||||
// As we just created the directory it will be empty
|
||||
return;
|
||||
}
|
||||
|
||||
List<Path> resourcePacks;
|
||||
try {
|
||||
resourcePacks = Files.walk(directory).collect(Collectors.toList());
|
||||
} catch (IOException e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Could not list packs directory", e);
|
||||
return;
|
||||
}
|
||||
|
||||
GeyserLoadResourcePacksEvent event = new GeyserLoadResourcePacksEvent(resourcePacks);
|
||||
GeyserImpl.getInstance().eventBus().fire(event);
|
||||
|
||||
for (Path path : event.resourcePacks()) {
|
||||
File file = path.toFile();
|
||||
|
||||
if (file.getName().endsWith(".zip") || file.getName().endsWith(".mcpack")) {
|
||||
ResourcePack pack = new ResourcePack();
|
||||
|
||||
pack.sha256 = FileUtils.calculateSHA256(file);
|
||||
|
||||
try (ZipFile zip = new ZipFile(file);
|
||||
Stream<? extends ZipEntry> stream = zip.stream()) {
|
||||
stream.forEach((x) -> {
|
||||
String name = x.getName();
|
||||
if (name.length() >= 80) {
|
||||
GeyserImpl.getInstance().getLogger().warning("The resource pack " + file.getName()
|
||||
+ " has a file in it that meets or exceeds 80 characters in its path (" + name
|
||||
+ ", " + name.length() + " characters long). This will cause problems on some Bedrock platforms." +
|
||||
" Please rename it to be shorter, or reduce the amount of folders needed to get to the file.");
|
||||
}
|
||||
if (name.contains("manifest.json")) {
|
||||
try {
|
||||
ResourcePackManifest manifest = FileUtils.loadJson(zip.getInputStream(x), ResourcePackManifest.class);
|
||||
// Sometimes a pack_manifest file is present and not in a valid format,
|
||||
// but a manifest file is, so we null check through that one
|
||||
if (manifest.getHeader().getUuid() != null) {
|
||||
pack.file = file;
|
||||
pack.manifest = manifest;
|
||||
pack.version = ResourcePackManifest.Version.fromArray(manifest.getHeader().getVersion());
|
||||
|
||||
PACKS.put(pack.getManifest().getHeader().getUuid().toString(), pack);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Check if a file exists with the same name as the resource pack suffixed by .key,
|
||||
// and set this as content key. (e.g. test.zip, key file would be test.zip.key)
|
||||
File keyFile = new File(file.getParentFile(), file.getName() + ".key");
|
||||
pack.contentKey = keyFile.exists() ? Files.readString(keyFile.toPath(), StandardCharsets.UTF_8) : "";
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", file.getName()));
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getSha256() {
|
||||
return sha256;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public ResourcePackManifest getManifest() {
|
||||
return manifest;
|
||||
}
|
||||
|
||||
public ResourcePackManifest.Version getVersion() {
|
||||
return version;
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.pack;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* author: NukkitX
|
||||
* Nukkit Project
|
||||
*/
|
||||
@Getter
|
||||
@EqualsAndHashCode
|
||||
public class ResourcePackManifest {
|
||||
@JsonProperty("format_version")
|
||||
private Integer formatVersion;
|
||||
private Header header;
|
||||
private Collection<Module> modules;
|
||||
protected Collection<Dependency> dependencies;
|
||||
|
||||
public Collection<Module> getModules() {
|
||||
return Collections.unmodifiableCollection(modules);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public static class Header {
|
||||
private String description;
|
||||
private String name;
|
||||
private UUID uuid;
|
||||
private int[] version;
|
||||
@JsonProperty("min_engine_version")
|
||||
private int[] minimumSupportedMinecraftVersion;
|
||||
|
||||
public String getVersionString() {
|
||||
return version[0] + "." + version[1] + "." + version[2];
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public static class Module {
|
||||
private String description;
|
||||
private String name;
|
||||
private UUID uuid;
|
||||
private int[] version;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public static class Dependency {
|
||||
private UUID uuid;
|
||||
private int[] version;
|
||||
}
|
||||
|
||||
@Value
|
||||
public static class Version {
|
||||
private final int major;
|
||||
private final int minor;
|
||||
private final int patch;
|
||||
|
||||
public static Version fromString(String ver) {
|
||||
String[] split = ver.replace(']', ' ')
|
||||
.replace('[', ' ')
|
||||
.replaceAll(" ", "").split(",");
|
||||
|
||||
return new Version(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2]));
|
||||
}
|
||||
|
||||
public static Version fromArray(int[] ver) {
|
||||
return new Version(ver[0], ver[1], ver[2]);
|
||||
}
|
||||
|
||||
private Version(int major, int minor, int patch) {
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return major + "." + minor + "." + patch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.pack.path;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.pack.PathPackCodec;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.registry.loader.ResourcePackLoader;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class GeyserPathPackCodec extends PathPackCodec {
|
||||
private final Path path;
|
||||
private FileTime lastModified;
|
||||
|
||||
private byte[] sha256;
|
||||
private long size = -1;
|
||||
|
||||
@Override
|
||||
public @NonNull Path path() {
|
||||
this.checkLastModified();
|
||||
return this.path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte @NonNull [] sha256() {
|
||||
this.checkLastModified();
|
||||
if (this.sha256 != null) {
|
||||
return this.sha256;
|
||||
}
|
||||
|
||||
return this.sha256 = FileUtils.calculateSHA256(this.path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() {
|
||||
this.checkLastModified();
|
||||
if (this.size != -1) {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
try {
|
||||
return this.size = Files.size(this.path);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not get file size of path " + this.path, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull SeekableByteChannel serialize(@NonNull ResourcePack resourcePack) throws IOException {
|
||||
return FileChannel.open(this.path);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull ResourcePack create() {
|
||||
return ResourcePackLoader.readPack(this.path);
|
||||
}
|
||||
|
||||
private void checkLastModified() {
|
||||
try {
|
||||
FileTime lastModified = Files.getLastModifiedTime(this.path);
|
||||
if (this.lastModified == null) {
|
||||
this.lastModified = lastModified;
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastModified.toInstant().isAfter(this.lastModified.toInstant())) {
|
||||
GeyserImpl.getInstance().getLogger().warning("Detected a change in the resource pack " + path + ". This is likely to cause undefined behavior for new clients joining. It is suggested you restart Geyser.");
|
||||
this.lastModified = lastModified;
|
||||
this.sha256 = null;
|
||||
this.size = -1;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.registry;
|
||||
|
||||
import org.geysermc.geyser.registry.loader.RegistryLoader;
|
||||
import org.geysermc.geyser.registry.loader.RegistryLoaders;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* A deferred registry is a registry that is not loaded until it is needed.
|
||||
* This is useful for registries that are not needed until after other parts
|
||||
* of the lifecycle have been completed.
|
||||
* <p>
|
||||
* This class is slightly different from other registries in that it acts as
|
||||
* a wrapper around another registry. This is to allow for any kind of registry
|
||||
* type to be deferred.
|
||||
*
|
||||
* @param <M> the value being held by the registry
|
||||
*/
|
||||
public final class DeferredRegistry<M> implements IRegistry<M> {
|
||||
private final Registry<M> backingRegistry;
|
||||
private final Supplier<M> loader;
|
||||
|
||||
private boolean loaded;
|
||||
|
||||
private <I> DeferredRegistry(Function<RegistryLoader<I, M>, Registry<M>> registryLoader, RegistryLoader<I, M> deferredLoader) {
|
||||
this.backingRegistry = registryLoader.apply(RegistryLoaders.uninitialized());
|
||||
this.loader = () -> deferredLoader.load(null);
|
||||
}
|
||||
|
||||
private <I> DeferredRegistry(Function<RegistryLoader<I, M>, Registry<M>> registryLoader, Supplier<RegistryLoader<I, M>> deferredLoader) {
|
||||
this.backingRegistry = registryLoader.apply(RegistryLoaders.uninitialized());
|
||||
this.loader = () -> deferredLoader.get().load(null);
|
||||
}
|
||||
|
||||
private <I> DeferredRegistry(I input, RegistryInitializer<M> registryInitializer, RegistryLoader<I, M> deferredLoader) {
|
||||
this.backingRegistry = registryInitializer.initialize(input, RegistryLoaders.uninitialized());
|
||||
this.loader = () -> deferredLoader.load(input);
|
||||
}
|
||||
|
||||
private <I> DeferredRegistry(I input, RegistryInitializer<M> registryInitializer, Supplier<RegistryLoader<I, M>> deferredLoader) {
|
||||
this.backingRegistry = registryInitializer.initialize(input, RegistryLoaders.uninitialized());
|
||||
this.loader = () -> deferredLoader.get().load(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public M get() {
|
||||
if (!this.loaded) {
|
||||
throw new IllegalStateException("Registry has not been loaded yet!");
|
||||
}
|
||||
|
||||
return this.backingRegistry.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(M mappings) {
|
||||
if (!this.loaded) {
|
||||
throw new IllegalStateException("Registry has not been loaded yet!");
|
||||
}
|
||||
|
||||
this.backingRegistry.set(mappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Consumer<M> consumer) {
|
||||
if (!this.loaded) {
|
||||
throw new IllegalStateException("Registry has not been loaded yet!");
|
||||
}
|
||||
|
||||
this.backingRegistry.register(consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the registry.
|
||||
*/
|
||||
public void load() {
|
||||
if (this.loaded) {
|
||||
throw new IllegalStateException("Registry has already been loaded!");
|
||||
}
|
||||
|
||||
this.backingRegistry.set(this.loader.get());
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new deferred registry.
|
||||
*
|
||||
* @param registryLoader the registry loader
|
||||
* @param deferredLoader the deferred loader
|
||||
* @param <I> the input type
|
||||
* @param <M> the registry type
|
||||
* @return the new deferred registry
|
||||
*/
|
||||
public static <I, M> DeferredRegistry<M> create(Function<RegistryLoader<I, M>, Registry<M>> registryLoader, RegistryLoader<I, M> deferredLoader) {
|
||||
return new DeferredRegistry<>(registryLoader, deferredLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new deferred registry.
|
||||
*
|
||||
* @param registryLoader the registry loader
|
||||
* @param deferredLoader the deferred loader
|
||||
* @param <I> the input type
|
||||
* @param <M> the registry type
|
||||
* @return the new deferred registry
|
||||
*/
|
||||
public static <I, M> DeferredRegistry<M> create(Function<RegistryLoader<I, M>, Registry<M>> registryLoader, Supplier<RegistryLoader<I, M>> deferredLoader) {
|
||||
return new DeferredRegistry<>(registryLoader, deferredLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new deferred registry.
|
||||
*
|
||||
* @param registryInitializer the registry initializer
|
||||
* @param deferredLoader the deferred loader
|
||||
* @param <I> the input type
|
||||
* @param <M> the registry type
|
||||
* @return the new deferred registry
|
||||
*/
|
||||
public static <I, M> DeferredRegistry<M> create(I input, RegistryInitializer<M> registryInitializer, RegistryLoader<I, M> deferredLoader) {
|
||||
return new DeferredRegistry<>(input, registryInitializer, deferredLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new deferred registry.
|
||||
*
|
||||
* @param registryInitializer the registry initializer
|
||||
* @param deferredLoader the deferred loader
|
||||
* @param <I> the input type
|
||||
* @param <M> the registry type
|
||||
* @return the new deferred registry
|
||||
*/
|
||||
public static <I, M> DeferredRegistry<M> create(I input, RegistryInitializer<M> registryInitializer, Supplier<RegistryLoader<I, M>> deferredLoader) {
|
||||
return new DeferredRegistry<>(input, registryInitializer, deferredLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* A registry initializer.
|
||||
*
|
||||
* @param <M> the registry type
|
||||
*/
|
||||
interface RegistryInitializer<M> {
|
||||
|
||||
/**
|
||||
* Initializes the registry.
|
||||
*
|
||||
* @param input the input
|
||||
* @param registryLoader the registry loader
|
||||
* @param <I> the input type
|
||||
* @return the initialized registry
|
||||
*/
|
||||
<I> Registry<M> initialize(I input, RegistryLoader<I, M> registryLoader);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.registry;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Represents a registry.
|
||||
*
|
||||
* @param <M> the value being held by the registry
|
||||
*/
|
||||
interface IRegistry<M> {
|
||||
|
||||
/**
|
||||
* Gets the underlying value held by this registry.
|
||||
*
|
||||
* @return the underlying value held by this registry.
|
||||
*/
|
||||
M get();
|
||||
|
||||
/**
|
||||
* Sets the underlying value held by this registry.
|
||||
* Clears any existing data associated with the previous
|
||||
* value.
|
||||
*
|
||||
* @param mappings the underlying value held by this registry
|
||||
*/
|
||||
void set(M mappings);
|
||||
|
||||
/**
|
||||
* Registers what is specified in the given
|
||||
* {@link Consumer} into the underlying value.
|
||||
*
|
||||
* @param consumer the consumer
|
||||
*/
|
||||
void register(Consumer<M> consumer);
|
||||
}
|
|
@ -41,6 +41,8 @@ import org.cloudburstmc.nbt.NbtMapBuilder;
|
|||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.PotionMixData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.RecipeData;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.inventory.item.Enchantment.JavaEnchantment;
|
||||
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
|
||||
|
@ -158,6 +160,11 @@ public final class Registries {
|
|||
*/
|
||||
public static final IntMappedRegistry<org.cloudburstmc.protocol.bedrock.data.SoundEvent> RECORDS = IntMappedRegistry.create(RegistryLoaders.empty(Int2ObjectOpenHashMap::new));
|
||||
|
||||
/**
|
||||
* A mapped registry holding {@link ResourcePack}'s with the pack uuid as keys.
|
||||
*/
|
||||
public static final DeferredRegistry<Map<String, ResourcePack>> RESOURCE_PACKS = DeferredRegistry.create(GeyserImpl.getInstance().packDirectory(), SimpleMappedRegistry::create, RegistryLoaders.RESOURCE_PACKS);
|
||||
|
||||
/**
|
||||
* A mapped registry holding sound identifiers to their corresponding {@link SoundMapping}.
|
||||
*/
|
||||
|
|
|
@ -64,7 +64,7 @@ import java.util.function.Consumer;
|
|||
*
|
||||
* @param <M> the value being held by the registry
|
||||
*/
|
||||
public abstract class Registry<M> {
|
||||
public abstract class Registry<M> implements IRegistry<M> {
|
||||
protected M mappings;
|
||||
|
||||
/**
|
||||
|
@ -85,6 +85,7 @@ public abstract class Registry<M> {
|
|||
*
|
||||
* @return the underlying value held by this registry.
|
||||
*/
|
||||
@Override
|
||||
public M get() {
|
||||
return this.mappings;
|
||||
}
|
||||
|
@ -96,6 +97,7 @@ public abstract class Registry<M> {
|
|||
*
|
||||
* @param mappings the underlying value held by this registry
|
||||
*/
|
||||
@Override
|
||||
public void set(M mappings) {
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
@ -106,6 +108,7 @@ public abstract class Registry<M> {
|
|||
*
|
||||
* @param consumer the consumer
|
||||
*/
|
||||
@Override
|
||||
public void register(Consumer<M> consumer) {
|
||||
consumer.accept(this.mappings);
|
||||
}
|
||||
|
|
|
@ -31,13 +31,16 @@ import org.geysermc.geyser.api.extension.Extension;
|
|||
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
||||
import org.geysermc.geyser.api.pack.PathPackCodec;
|
||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||
import org.geysermc.geyser.event.GeyserEventRegistrar;
|
||||
import org.geysermc.geyser.item.GeyserCustomItemData;
|
||||
import org.geysermc.geyser.item.GeyserCustomItemOptions;
|
||||
import org.geysermc.geyser.item.GeyserNonVanillaCustomItemData;
|
||||
import org.geysermc.geyser.pack.path.GeyserPathPackCodec;
|
||||
import org.geysermc.geyser.registry.provider.ProviderSupplier;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -47,11 +50,15 @@ public class ProviderRegistryLoader implements RegistryLoader<Map<Class<?>, Prov
|
|||
|
||||
@Override
|
||||
public Map<Class<?>, ProviderSupplier> load(Map<Class<?>, ProviderSupplier> providers) {
|
||||
// misc
|
||||
providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Extension) args[0]));
|
||||
providers.put(EventRegistrar.class, args -> new GeyserEventRegistrar(args[0]));
|
||||
providers.put(PathPackCodec.class, args -> new GeyserPathPackCodec((Path) args[0]));
|
||||
|
||||
// items
|
||||
providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder());
|
||||
providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder());
|
||||
providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder());
|
||||
providers.put(EventRegistrar.class, args -> new GeyserEventRegistrar(args[0]));
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,12 @@ public final class RegistryLoaders {
|
|||
/**
|
||||
* The {@link RegistryLoader} responsible for loading NBT.
|
||||
*/
|
||||
public static NbtRegistryLoader NBT = new NbtRegistryLoader();
|
||||
public static final NbtRegistryLoader NBT = new NbtRegistryLoader();
|
||||
|
||||
/**
|
||||
* The {@link RegistryLoader} responsible for loading resource packs.
|
||||
*/
|
||||
public static final ResourcePackLoader RESOURCE_PACKS = new ResourcePackLoader();
|
||||
|
||||
/**
|
||||
* Wraps the surrounding {@link Supplier} in a {@link RegistryLoader} which does
|
||||
|
@ -51,10 +56,14 @@ public final class RegistryLoaders {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link RegistryLoader} which has not taken
|
||||
* in any input value.
|
||||
*
|
||||
* @param <I> the input
|
||||
* @param <V> the value
|
||||
* @return a RegistryLoader that is yet to contain a value.
|
||||
*/
|
||||
public static <V> RegistryLoader<Object, V> uninitialized() {
|
||||
public static <I, V> RegistryLoader<I, V> uninitialized() {
|
||||
return input -> null;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.registry.loader;
|
||||
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.event.lifecycle.GeyserLoadResourcePacksEvent;
|
||||
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||
import org.geysermc.geyser.pack.GeyserResourcePack;
|
||||
import org.geysermc.geyser.pack.GeyserResourcePackManifest;
|
||||
import org.geysermc.geyser.pack.path.GeyserPathPackCodec;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.util.FileUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* Loads {@link ResourcePack}s within a {@link Path} directory, firing the {@link GeyserLoadResourcePacksEvent}.
|
||||
*/
|
||||
public class ResourcePackLoader implements RegistryLoader<Path, Map<String, ResourcePack>> {
|
||||
|
||||
static final PathMatcher PACK_MATCHER = FileSystems.getDefault().getPathMatcher("glob:**.{zip,mcpack}");
|
||||
|
||||
private static final boolean SHOW_RESOURCE_PACK_LENGTH_WARNING = Boolean.parseBoolean(System.getProperty("Geyser.ShowResourcePackLengthWarning", "true"));
|
||||
|
||||
/**
|
||||
* Loop through the packs directory and locate valid resource pack files
|
||||
*/
|
||||
@Override
|
||||
public Map<String, ResourcePack> load(Path directory) {
|
||||
Map<String, ResourcePack> packMap = new HashMap<>();
|
||||
|
||||
if (!Files.exists(directory)) {
|
||||
try {
|
||||
Files.createDirectory(directory);
|
||||
} catch (IOException e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Could not create packs directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
List<Path> resourcePacks;
|
||||
try (Stream<Path> stream = Files.walk(directory)) {
|
||||
resourcePacks = stream.filter(PACK_MATCHER::matches)
|
||||
.collect(Collectors.toCollection(ArrayList::new)); // toList() does not guarantee mutability
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Could not list packs directory", e);
|
||||
|
||||
// Ensure the event is fired even if there was an issue reading
|
||||
// from our own resource pack directory. External projects may have
|
||||
// resource packs located at different locations.
|
||||
resourcePacks = new ArrayList<>();
|
||||
}
|
||||
|
||||
GeyserLoadResourcePacksEvent event = new GeyserLoadResourcePacksEvent(resourcePacks);
|
||||
GeyserImpl.getInstance().eventBus().fire(event);
|
||||
|
||||
for (Path path : event.resourcePacks()) {
|
||||
try {
|
||||
GeyserResourcePack pack = readPack(path);
|
||||
packMap.put(pack.manifest().header().uuid().toString(), pack);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return packMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a resource pack at the given file. Also searches for a file in the same directory, with the same name
|
||||
* but suffixed by ".key", containing the content key. If such file does not exist, no content key is stored.
|
||||
*
|
||||
* @param path the file to read from, in ZIP format
|
||||
* @return a {@link ResourcePack} representation
|
||||
* @throws IllegalArgumentException if the pack manifest was invalid or there was any processing exception
|
||||
*/
|
||||
public static GeyserResourcePack readPack(Path path) throws IllegalArgumentException {
|
||||
if (!path.getFileName().toString().endsWith(".mcpack") && !path.getFileName().toString().endsWith(".zip")) {
|
||||
throw new IllegalArgumentException("Resource pack " + path.getFileName() + " must be a .zip or .mcpack file!");
|
||||
}
|
||||
|
||||
AtomicReference<GeyserResourcePackManifest> manifestReference = new AtomicReference<>();
|
||||
|
||||
try (ZipFile zip = new ZipFile(path.toFile());
|
||||
Stream<? extends ZipEntry> stream = zip.stream()) {
|
||||
stream.forEach(x -> {
|
||||
String name = x.getName();
|
||||
if (SHOW_RESOURCE_PACK_LENGTH_WARNING && name.length() >= 80) {
|
||||
GeyserImpl.getInstance().getLogger().warning("The resource pack " + path.getFileName()
|
||||
+ " has a file in it that meets or exceeds 80 characters in its path (" + name
|
||||
+ ", " + name.length() + " characters long). This will cause problems on some Bedrock platforms." +
|
||||
" Please rename it to be shorter, or reduce the amount of folders needed to get to the file.");
|
||||
}
|
||||
if (name.contains("manifest.json")) {
|
||||
try {
|
||||
GeyserResourcePackManifest manifest = FileUtils.loadJson(zip.getInputStream(x), GeyserResourcePackManifest.class);
|
||||
if (manifest.header().uuid() != null) {
|
||||
manifestReference.set(manifest);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
GeyserResourcePackManifest manifest = manifestReference.get();
|
||||
if (manifest == null) {
|
||||
throw new IllegalArgumentException(path.getFileName() + " does not contain a valid pack_manifest.json or manifest.json");
|
||||
}
|
||||
|
||||
// Check if a file exists with the same name as the resource pack suffixed by .key,
|
||||
// and set this as content key. (e.g. test.zip, key file would be test.zip.key)
|
||||
Path keyFile = path.resolveSibling(path.getFileName().toString() + ".key");
|
||||
String contentKey = Files.exists(keyFile) ? Files.readString(path, StandardCharsets.UTF_8) : "";
|
||||
|
||||
return new GeyserResourcePack(new GeyserPathPackCodec(path), manifest, contentKey);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", path.getFileName()), e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,10 +30,9 @@ import org.geysermc.geyser.util.AssetUtils;
|
|||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
@ -80,14 +79,14 @@ public final class ProvidedSkins {
|
|||
.resolve(slim ? "slim" : "wide");
|
||||
String assetName = asset.substring(asset.lastIndexOf('/') + 1);
|
||||
|
||||
File location = folder.resolve(assetName).toFile();
|
||||
AssetUtils.addTask(!location.exists(), new AssetUtils.ClientJarTask("assets/minecraft/" + asset,
|
||||
Path location = folder.resolve(assetName);
|
||||
AssetUtils.addTask(!Files.exists(location), new AssetUtils.ClientJarTask("assets/minecraft/" + asset,
|
||||
(stream) -> AssetUtils.saveFile(location, stream),
|
||||
() -> {
|
||||
try {
|
||||
// TODO lazy initialize?
|
||||
BufferedImage image;
|
||||
try (InputStream stream = new FileInputStream(location)) {
|
||||
try (InputStream stream = Files.newInputStream(location)) {
|
||||
image = ImageIO.read(stream);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.geysermc.geyser.util.WebUtils;
|
|||
import javax.annotation.Nullable;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
@ -57,8 +58,8 @@ public class MinecraftLocale {
|
|||
}
|
||||
|
||||
public static void ensureEN_US() {
|
||||
File localeFile = getFile("en_us");
|
||||
AssetUtils.addTask(!localeFile.exists(), new AssetUtils.ClientJarTask("assets/minecraft/lang/en_us.json",
|
||||
Path localeFile = getPath("en_us");
|
||||
AssetUtils.addTask(!Files.exists(localeFile), new AssetUtils.ClientJarTask("assets/minecraft/lang/en_us.json",
|
||||
(stream) -> AssetUtils.saveFile(localeFile, stream),
|
||||
() -> {
|
||||
if ("en_us".equals(GeyserLocale.getDefaultLocale())) {
|
||||
|
@ -106,10 +107,10 @@ public class MinecraftLocale {
|
|||
if (locale.equals("en_us")) {
|
||||
return;
|
||||
}
|
||||
File localeFile = getFile(locale);
|
||||
Path localeFile = getPath(locale);
|
||||
|
||||
// Check if we have already downloaded the locale file
|
||||
if (localeFile.exists()) {
|
||||
if (Files.exists(localeFile)) {
|
||||
String curHash = byteArrayToHexString(FileUtils.calculateSHA1(localeFile));
|
||||
String targetHash = AssetUtils.getAsset("minecraft/lang/" + locale + ".json").getHash();
|
||||
|
||||
|
@ -130,8 +131,8 @@ public class MinecraftLocale {
|
|||
}
|
||||
}
|
||||
|
||||
private static File getFile(String locale) {
|
||||
return GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales/" + locale + ".json").toFile();
|
||||
private static Path getPath(String locale) {
|
||||
return GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("locales/" + locale + ".json");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -192,8 +192,8 @@ public final class AssetUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static void saveFile(File location, InputStream fileStream) throws IOException {
|
||||
try (FileOutputStream outStream = new FileOutputStream(location)) {
|
||||
public static void saveFile(Path location, InputStream fileStream) throws IOException {
|
||||
try (OutputStream outStream = Files.newOutputStream(location)) {
|
||||
|
||||
// Write the file to the locale dir
|
||||
byte[] buf = new byte[fileStream.available()];
|
||||
|
|
|
@ -129,14 +129,14 @@ public class FileUtils {
|
|||
/**
|
||||
* Calculate the SHA256 hash of a file
|
||||
*
|
||||
* @param file File to calculate the hash for
|
||||
* @param path Path to calculate the hash for
|
||||
* @return A byte[] representation of the hash
|
||||
*/
|
||||
public static byte[] calculateSHA256(File file) {
|
||||
public static byte[] calculateSHA256(Path path) {
|
||||
byte[] sha256;
|
||||
|
||||
try {
|
||||
sha256 = MessageDigest.getInstance("SHA-256").digest(readAllBytes(file));
|
||||
sha256 = MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(path));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not calculate pack hash", e);
|
||||
}
|
||||
|
@ -147,14 +147,14 @@ public class FileUtils {
|
|||
/**
|
||||
* Calculate the SHA1 hash of a file
|
||||
*
|
||||
* @param file File to calculate the hash for
|
||||
* @param path Path to calculate the hash for
|
||||
* @return A byte[] representation of the hash
|
||||
*/
|
||||
public static byte[] calculateSHA1(File file) {
|
||||
public static byte[] calculateSHA1(Path path) {
|
||||
byte[] sha1;
|
||||
|
||||
try {
|
||||
sha1 = MessageDigest.getInstance("SHA-1").digest(readAllBytes(file));
|
||||
sha1 = MessageDigest.getInstance("SHA-1").digest(Files.readAllBytes(path));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not calculate pack hash", e);
|
||||
}
|
||||
|
@ -162,20 +162,6 @@ public class FileUtils {
|
|||
return sha1;
|
||||
}
|
||||
|
||||
/**
|
||||
* An android compatible version of {@link Files#readAllBytes}
|
||||
*
|
||||
* @param file File to read bytes of
|
||||
* @return The byte array of the file
|
||||
*/
|
||||
public static byte[] readAllBytes(File file) {
|
||||
try (InputStream stream = new FileInputStream(file)) {
|
||||
return stream.readAllBytes();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot read " + file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource the internal resource to read off from
|
||||
* @return the byte array of an InputStream
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.registry.loader;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class ResourcePackLoaderTest {
|
||||
|
||||
@Test
|
||||
public void testPathMatcher() {
|
||||
PathMatcher matcher = ResourcePackLoader.PACK_MATCHER;
|
||||
|
||||
assertTrue(matcher.matches(Path.of("pack.mcpack")));
|
||||
assertTrue(matcher.matches(Path.of("pack.zip")));
|
||||
assertTrue(matcher.matches(Path.of("packs", "pack.mcpack")));
|
||||
assertTrue(matcher.matches(Path.of("packs", "category", "pack.mcpack")));
|
||||
|
||||
assertTrue(matcher.matches(Path.of("packs", "Resource+Pack+1.2.3.mcpack")));
|
||||
assertTrue(matcher.matches(Path.of("Resource+Pack+1.2.3.mcpack")));
|
||||
|
||||
assertTrue(matcher.matches(Path.of("packs", "Resource+Pack+1.2.3.zip")));
|
||||
assertTrue(matcher.matches(Path.of("Resource+Pack+1.2.3.zip")));
|
||||
|
||||
assertFalse(matcher.matches(Path.of("resource.pack")));
|
||||
assertFalse(matcher.matches(Path.of("pack.7zip")));
|
||||
assertFalse(matcher.matches(Path.of("packs")));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue