forked from GeyserMC/Geyser
Add support for manually supplying Bedrock resource packs (#1076)
* send resource packs A lot of this code is nukkit-credits in the classes * send resource packs A lot of this code is nukkit-credits in the classes * Remove unnecessary code/debugs * use separately generated hashes * Updated mappings and added .mcpack support * "packs" directory auto-create (#484) * "packs" directory auto-create * cleaned indentation in ResourcePack.java * Cleaned ResourcePack.java * Another cleanup I hate editor on github. * Yet another * Another indentation cleanup * Fix resource pack loading (cherry picked from commitf93b07491e
) * Move back to internal sha256 hashing (cherry picked from commit812a3d82b2
) * Add resource pack loading back after merge * Add comments, config option and removed unused files * Fix packs folder location and cleanup code * Move to better options for the client * Fix typos in comments * Fix pack loading * Try to make it compile * Final touches? * Add Javadoc for MathUtils#constrain Co-authored-by: EOT3000 <43685885+EOT3000@users.noreply.github.com> Co-authored-by: Vesek <61123478+Vesek@users.noreply.github.com> Co-authored-by: Heath123 <heath.mitchell27@gmail.com> Co-authored-by: Camotoy <20743703+DoctorMacc@users.noreply.github.com>
This commit is contained in:
parent
1a49e882d3
commit
99e72f35b3
10 changed files with 373 additions and 10 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -242,3 +242,4 @@ logs/
|
||||||
public-key.pem
|
public-key.pem
|
||||||
locales/
|
locales/
|
||||||
/cache/
|
/cache/
|
||||||
|
/packs/
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
package org.geysermc.connector;
|
package org.geysermc.connector;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.nukkitx.network.raknet.RakNetConstants;
|
import com.nukkitx.network.raknet.RakNetConstants;
|
||||||
|
@ -56,6 +57,7 @@ import org.geysermc.connector.network.translators.world.block.entity.BlockEntity
|
||||||
import org.geysermc.connector.utils.DimensionUtils;
|
import org.geysermc.connector.utils.DimensionUtils;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.LanguageUtils;
|
||||||
import org.geysermc.connector.utils.LocaleUtils;
|
import org.geysermc.connector.utils.LocaleUtils;
|
||||||
|
import org.geysermc.connector.utils.ResourcePack;
|
||||||
|
|
||||||
import javax.naming.directory.Attribute;
|
import javax.naming.directory.Attribute;
|
||||||
import javax.naming.directory.InitialDirContext;
|
import javax.naming.directory.InitialDirContext;
|
||||||
|
@ -73,7 +75,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
@Getter
|
@Getter
|
||||||
public class GeyserConnector {
|
public class GeyserConnector {
|
||||||
|
|
||||||
public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
|
public static final ObjectMapper JSON_MAPPER = new ObjectMapper().enable(JsonParser.Feature.IGNORE_UNDEFINED).enable(JsonParser.Feature.ALLOW_COMMENTS).disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||||
|
|
||||||
public static final String NAME = "Geyser";
|
public static final String NAME = "Geyser";
|
||||||
public static final String VERSION = "DEV"; // A fallback for running in IDEs
|
public static final String VERSION = "DEV"; // A fallback for running in IDEs
|
||||||
|
@ -136,6 +138,8 @@ public class GeyserConnector {
|
||||||
SoundRegistry.init();
|
SoundRegistry.init();
|
||||||
SoundHandlerRegistry.init();
|
SoundHandlerRegistry.init();
|
||||||
|
|
||||||
|
ResourcePack.loadPacks();
|
||||||
|
|
||||||
if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) {
|
if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) {
|
||||||
// Set the remote address to localhost since that is where we are always connecting
|
// Set the remote address to localhost since that is where we are always connecting
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -79,6 +79,8 @@ public interface GeyserConfiguration {
|
||||||
|
|
||||||
boolean isCacheChunks();
|
boolean isCacheChunks();
|
||||||
|
|
||||||
|
boolean isForceResourcePacks();
|
||||||
|
|
||||||
int getCacheImages();
|
int getCacheImages();
|
||||||
|
|
||||||
IMetricsInfo getMetrics();
|
IMetricsInfo getMetrics();
|
||||||
|
|
|
@ -103,6 +103,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||||
@JsonProperty("above-bedrock-nether-building")
|
@JsonProperty("above-bedrock-nether-building")
|
||||||
private boolean aboveBedrockNetherBuilding = false;
|
private boolean aboveBedrockNetherBuilding = false;
|
||||||
|
|
||||||
|
@JsonProperty("force-resource-packs")
|
||||||
|
private boolean forceResourcePacks = true;
|
||||||
|
|
||||||
private MetricsInfo metrics;
|
private MetricsInfo metrics;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
|
|
@ -26,17 +26,24 @@
|
||||||
package org.geysermc.connector.network;
|
package org.geysermc.connector.network;
|
||||||
|
|
||||||
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.ResourcePackType;
|
||||||
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
import com.nukkitx.protocol.bedrock.packet.*;
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.common.AuthType;
|
import org.geysermc.connector.common.AuthType;
|
||||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
||||||
import org.geysermc.connector.utils.LoginEncryptionUtils;
|
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.LanguageUtils;
|
||||||
|
import org.geysermc.connector.utils.LoginEncryptionUtils;
|
||||||
|
import org.geysermc.connector.utils.MathUtils;
|
||||||
|
import org.geysermc.connector.utils.ResourcePack;
|
||||||
|
import org.geysermc.connector.utils.ResourcePackManifest;
|
||||||
import org.geysermc.connector.utils.SettingsUtils;
|
import org.geysermc.connector.utils.SettingsUtils;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
public class UpstreamPacketHandler extends LoggingPacketHandler {
|
public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
|
|
||||||
public UpstreamPacketHandler(GeyserConnector connector, GeyserSession session) {
|
public UpstreamPacketHandler(GeyserConnector connector, GeyserSession session) {
|
||||||
|
@ -70,6 +77,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
session.sendUpstreamPacket(playStatus);
|
session.sendUpstreamPacket(playStatus);
|
||||||
|
|
||||||
ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
|
ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
|
||||||
|
for(ResourcePack resourcePack : ResourcePack.PACKS.values()) {
|
||||||
|
ResourcePackManifest.Header header = resourcePack.getManifest().getHeader();
|
||||||
|
resourcePacksInfo.getResourcePackInfos().add(new ResourcePacksInfoPacket.Entry(header.getUuid().toString(), header.getVersionString(), resourcePack.getFile().length(), "", "", "", false));
|
||||||
|
}
|
||||||
|
resourcePacksInfo.setForcedToAccept(GeyserConnector.getInstance().getConfig().isForceResourcePacks());
|
||||||
session.sendUpstreamPacket(resourcePacksInfo);
|
session.sendUpstreamPacket(resourcePacksInfo);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -81,13 +93,42 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
session.connect(connector.getRemoteServer());
|
session.connect(connector.getRemoteServer());
|
||||||
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.connect", session.getAuthData().getName()));
|
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.connect", session.getAuthData().getName()));
|
||||||
break;
|
break;
|
||||||
case HAVE_ALL_PACKS:
|
|
||||||
ResourcePackStackPacket stack = new ResourcePackStackPacket();
|
case SEND_PACKS:
|
||||||
stack.setExperimental(false);
|
for(String id : packet.getPackIds()) {
|
||||||
stack.setForcedToAccept(false);
|
ResourcePackDataInfoPacket data = new ResourcePackDataInfoPacket();
|
||||||
stack.setGameVersion("*");
|
String[] packID = id.split("_");
|
||||||
session.sendUpstreamPacket(stack);
|
ResourcePack pack = ResourcePack.PACKS.get(packID[0]);
|
||||||
|
ResourcePackManifest.Header header = pack.getManifest().getHeader();
|
||||||
|
|
||||||
|
data.setPackId(header.getUuid());
|
||||||
|
int chunkCount = (int) Math.ceil((int) pack.getFile().length() / (double) ResourcePack.CHUNK_SIZE);
|
||||||
|
data.setChunkCount(chunkCount);
|
||||||
|
data.setCompressedPackSize(pack.getFile().length());
|
||||||
|
data.setMaxChunkSize(ResourcePack.CHUNK_SIZE);
|
||||||
|
data.setHash(pack.getSha256());
|
||||||
|
data.setPackVersion(packID[1]);
|
||||||
|
data.setPremium(false);
|
||||||
|
data.setType(ResourcePackType.RESOURCE);
|
||||||
|
|
||||||
|
session.sendUpstreamPacket(data);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HAVE_ALL_PACKS:
|
||||||
|
ResourcePackStackPacket stackPacket = new ResourcePackStackPacket();
|
||||||
|
stackPacket.setExperimental(false);
|
||||||
|
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(), ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
session.sendUpstreamPacket(stackPacket);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
session.disconnect("disconnectionScreen.resourcePack");
|
session.disconnect("disconnectionScreen.resourcePack");
|
||||||
break;
|
break;
|
||||||
|
@ -149,4 +190,30 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
boolean defaultHandler(BedrockPacket packet) {
|
boolean defaultHandler(BedrockPacket packet) {
|
||||||
return translateAndDefault(packet);
|
return translateAndDefault(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handle(ResourcePackChunkRequestPacket packet) {
|
||||||
|
ResourcePackChunkDataPacket data = new ResourcePackChunkDataPacket();
|
||||||
|
ResourcePack pack = ResourcePack.PACKS.get(packet.getPackId().toString());
|
||||||
|
|
||||||
|
data.setChunkIndex(packet.getChunkIndex());
|
||||||
|
data.setProgress(packet.getChunkIndex() * ResourcePack.CHUNK_SIZE);
|
||||||
|
data.setPackVersion(packet.getPackVersion());
|
||||||
|
data.setPackId(packet.getPackId());
|
||||||
|
|
||||||
|
int offset = packet.getChunkIndex() * ResourcePack.CHUNK_SIZE;
|
||||||
|
byte[] packData = new byte[(int) MathUtils.constrain(pack.getFile().length() - offset, 0, ResourcePack.CHUNK_SIZE)];
|
||||||
|
|
||||||
|
try (InputStream inputStream = new FileInputStream(pack.getFile())) {
|
||||||
|
inputStream.skip(offset);
|
||||||
|
inputStream.read(packData, 0, packData.length);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
data.setData(packData);
|
||||||
|
|
||||||
|
session.sendUpstreamPacket(data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
package org.geysermc.connector.utils;
|
package org.geysermc.connector.utils;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
@ -37,6 +39,8 @@ import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.security.MessageDigest;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class FileUtils {
|
public class FileUtils {
|
||||||
|
@ -55,6 +59,15 @@ public class FileUtils {
|
||||||
return objectMapper.readValue(src, valueType);
|
return objectMapper.readValue(src, valueType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> T loadYaml(InputStream src, Class<T> valueType) throws IOException {
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()).enable(JsonParser.Feature.IGNORE_UNDEFINED).disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||||
|
return objectMapper.readValue(src, valueType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T loadJson(InputStream src, Class<T> valueType) throws IOException {
|
||||||
|
return GeyserConnector.JSON_MAPPER.readValue(src, valueType);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the specified file or copy if from resources
|
* Open the specified file or copy if from resources
|
||||||
*
|
*
|
||||||
|
@ -145,6 +158,23 @@ public class FileUtils {
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the SHA256 hash of the resource pack file
|
||||||
|
* @param file File to calculate the hash for
|
||||||
|
* @return A byte[] representation of the hash
|
||||||
|
*/
|
||||||
|
public static byte[] calculateSHA256(File file) {
|
||||||
|
byte[] sha256;
|
||||||
|
|
||||||
|
try {
|
||||||
|
sha256 = MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(file.toPath()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Could not calculate pack hash", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sha256;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the stored reflection data for a given path
|
* Get the stored reflection data for a given path
|
||||||
*
|
*
|
||||||
|
|
|
@ -40,6 +40,26 @@ public class MathUtils {
|
||||||
return floatNumber > truncated ? truncated + 1 : truncated;
|
return floatNumber > truncated ? truncated + 1 : truncated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If number is greater than the max, set it to max, and if number is lower than low, set it to low.
|
||||||
|
* @param num number to calculate
|
||||||
|
* @param min the lowest value the number can be
|
||||||
|
* @param max the greatest value the number can be
|
||||||
|
* @return - min if num is lower than min <br>
|
||||||
|
* - max if num is greater than max <br>
|
||||||
|
* - num otherwise
|
||||||
|
*/
|
||||||
|
public static double constrain(double num, double min, double max) {
|
||||||
|
if (num > max) {
|
||||||
|
num = max;
|
||||||
|
}
|
||||||
|
if (num < min) {
|
||||||
|
num = min;
|
||||||
|
}
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the given object from an int or byte to byte.
|
* Converts the given object from an int or byte to byte.
|
||||||
* This is used for NBT data that might be either an int
|
* This is used for NBT data that might be either an int
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020 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.connector.utils;
|
||||||
|
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loop through the packs directory and locate valid resource pack files
|
||||||
|
*/
|
||||||
|
public static void loadPacks() {
|
||||||
|
File directory = GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("packs").toFile();
|
||||||
|
|
||||||
|
if (!directory.exists()) {
|
||||||
|
directory.mkdir();
|
||||||
|
|
||||||
|
// As we just created the directory it will be empty
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (File file : directory.listFiles()) {
|
||||||
|
if (file.getName().endsWith(".zip") || file.getName().endsWith(".mcpack")) {
|
||||||
|
ResourcePack pack = new ResourcePack();
|
||||||
|
|
||||||
|
pack.sha256 = FileUtils.calculateSHA256(file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ZipFile zip = new ZipFile(file);
|
||||||
|
|
||||||
|
zip.stream().forEach((x) -> {
|
||||||
|
if (x.getName().contains("manifest.json")) {
|
||||||
|
try {
|
||||||
|
ResourcePackManifest manifest = FileUtils.loadJson(zip.getInputStream(x), ResourcePackManifest.class);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
GeyserConnector.getInstance().getLogger().error(LanguageUtils.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020 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.connector.utils;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -112,6 +112,11 @@ cache-images: 0
|
||||||
# the end sky in the nether, but ultimately it's the only way for this feature to work.
|
# the end sky in the nether, but ultimately it's the only way for this feature to work.
|
||||||
above-bedrock-nether-building: false
|
above-bedrock-nether-building: false
|
||||||
|
|
||||||
|
# Force clients to load all resource packs if there are any.
|
||||||
|
# If set to false it allows the user to disconnect from the server if they don't
|
||||||
|
# want to download the resource packs
|
||||||
|
force-resource-packs: true
|
||||||
|
|
||||||
# bStats is a stat tracker that is entirely anonymous and tracks only basic information
|
# bStats is a stat tracker that is entirely anonymous and tracks only basic information
|
||||||
# about Geyser, such as how many people are online, how many servers are using Geyser,
|
# about Geyser, such as how many people are online, how many servers are using Geyser,
|
||||||
# what OS is being used, etc. You can learn more about bStats here: https://bstats.org/.
|
# what OS is being used, etc. You can learn more about bStats here: https://bstats.org/.
|
||||||
|
|
Loading…
Reference in a new issue