base mod created
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.client.resources.data.IMetadataSection;
|
||||
import net.minecraft.client.resources.data.MetadataSerializer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public abstract class AbstractResourcePack implements IResourcePack
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
protected final File resourcePackFile;
|
||||
|
||||
public AbstractResourcePack(File resourcePackFileIn)
|
||||
{
|
||||
this.resourcePackFile = resourcePackFileIn;
|
||||
}
|
||||
|
||||
private static String locationToName(ResourceLocation location)
|
||||
{
|
||||
return String.format("%s/%s/%s", "assets", location.getResourceDomain(), location.getResourcePath());
|
||||
}
|
||||
|
||||
protected static String getRelativeName(File p_110595_0_, File p_110595_1_)
|
||||
{
|
||||
return p_110595_0_.toURI().relativize(p_110595_1_.toURI()).getPath();
|
||||
}
|
||||
|
||||
public InputStream getInputStream(ResourceLocation location) throws IOException
|
||||
{
|
||||
return this.getInputStreamByName(locationToName(location));
|
||||
}
|
||||
|
||||
public boolean resourceExists(ResourceLocation location)
|
||||
{
|
||||
return this.hasResourceName(locationToName(location));
|
||||
}
|
||||
|
||||
protected abstract InputStream getInputStreamByName(String name) throws IOException;
|
||||
|
||||
protected abstract boolean hasResourceName(String name);
|
||||
|
||||
protected void logNameNotLowercase(String name)
|
||||
{
|
||||
LOGGER.warn("ResourcePack: ignored non-lowercase namespace: {} in {}", name, this.resourcePackFile);
|
||||
}
|
||||
|
||||
public <T extends IMetadataSection> T getPackMetadata(MetadataSerializer metadataSerializer, String metadataSectionName) throws IOException
|
||||
{
|
||||
return (T)readMetadata(metadataSerializer, this.getInputStreamByName("pack.mcmeta"), metadataSectionName);
|
||||
}
|
||||
|
||||
static <T extends IMetadataSection> T readMetadata(MetadataSerializer metadataSerializer, InputStream p_110596_1_, String sectionName)
|
||||
{
|
||||
JsonObject jsonobject = null;
|
||||
BufferedReader bufferedreader = null;
|
||||
|
||||
try
|
||||
{
|
||||
bufferedreader = new BufferedReader(new InputStreamReader(p_110596_1_, StandardCharsets.UTF_8));
|
||||
jsonobject = (new JsonParser()).parse(bufferedreader).getAsJsonObject();
|
||||
}
|
||||
catch (RuntimeException runtimeexception)
|
||||
{
|
||||
throw new JsonParseException(runtimeexception);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly((Reader)bufferedreader);
|
||||
}
|
||||
|
||||
return (T)metadataSerializer.parseMetadataSection(sectionName, jsonobject);
|
||||
}
|
||||
|
||||
public BufferedImage getPackImage() throws IOException
|
||||
{
|
||||
return TextureUtil.readBufferedImage(this.getInputStreamByName("pack.png"));
|
||||
}
|
||||
|
||||
public String getPackName()
|
||||
{
|
||||
return this.resourcePackFile.getName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import java.util.UUID;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class DefaultPlayerSkin
|
||||
{
|
||||
/** The default skin for the Steve model. */
|
||||
private static final ResourceLocation TEXTURE_STEVE = new ResourceLocation("textures/entity/steve.png");
|
||||
/** The default skin for the Alex model. */
|
||||
private static final ResourceLocation TEXTURE_ALEX = new ResourceLocation("textures/entity/alex.png");
|
||||
|
||||
/**
|
||||
* Returns the default skind for versions prior to 1.8, which is always the Steve texture.
|
||||
*/
|
||||
public static ResourceLocation getDefaultSkinLegacy()
|
||||
{
|
||||
return TEXTURE_STEVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the default skin for this player. Depending on the model used this will be Alex or Steve.
|
||||
*/
|
||||
public static ResourceLocation getDefaultSkin(UUID playerUUID)
|
||||
{
|
||||
return isSlimSkin(playerUUID) ? TEXTURE_ALEX : TEXTURE_STEVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type of skin that a player is using. The Alex model is slim while the Steve model is default.
|
||||
*/
|
||||
public static String getSkinType(UUID playerUUID)
|
||||
{
|
||||
return isSlimSkin(playerUUID) ? "slim" : "default";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a players skin model is slim or the default. The Alex model is slime while the Steve model is default.
|
||||
*/
|
||||
private static boolean isSlimSkin(UUID playerUUID)
|
||||
{
|
||||
return (playerUUID.hashCode() & 1) == 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.client.resources.data.IMetadataSection;
|
||||
import net.minecraft.client.resources.data.MetadataSerializer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class DefaultResourcePack implements IResourcePack
|
||||
{
|
||||
public static final Set<String> DEFAULT_RESOURCE_DOMAINS = ImmutableSet.<String>of("minecraft", "realms");
|
||||
private final ResourceIndex resourceIndex;
|
||||
|
||||
public DefaultResourcePack(ResourceIndex resourceIndexIn)
|
||||
{
|
||||
this.resourceIndex = resourceIndexIn;
|
||||
}
|
||||
|
||||
public InputStream getInputStream(ResourceLocation location) throws IOException
|
||||
{
|
||||
InputStream inputstream = this.getInputStreamAssets(location);
|
||||
|
||||
if (inputstream != null)
|
||||
{
|
||||
return inputstream;
|
||||
}
|
||||
else
|
||||
{
|
||||
InputStream inputstream1 = this.getResourceStream(location);
|
||||
|
||||
if (inputstream1 != null)
|
||||
{
|
||||
return inputstream1;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException(location.getResourcePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public InputStream getInputStreamAssets(ResourceLocation location) throws IOException, FileNotFoundException
|
||||
{
|
||||
File file1 = this.resourceIndex.getFile(location);
|
||||
return file1 != null && file1.isFile() ? new FileInputStream(file1) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private InputStream getResourceStream(ResourceLocation location)
|
||||
{
|
||||
String s = "/assets/" + location.getResourceDomain() + "/" + location.getResourcePath();
|
||||
|
||||
try
|
||||
{
|
||||
URL url = DefaultResourcePack.class.getResource(s);
|
||||
return url != null && FolderResourcePack.validatePath(new File(url.getFile()), s) ? DefaultResourcePack.class.getResourceAsStream(s) : null;
|
||||
}
|
||||
catch (IOException var4)
|
||||
{
|
||||
return DefaultResourcePack.class.getResourceAsStream(s);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean resourceExists(ResourceLocation location)
|
||||
{
|
||||
return this.getResourceStream(location) != null || this.resourceIndex.isFileExisting(location);
|
||||
}
|
||||
|
||||
public Set<String> getResourceDomains()
|
||||
{
|
||||
return DEFAULT_RESOURCE_DOMAINS;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T extends IMetadataSection> T getPackMetadata(MetadataSerializer metadataSerializer, String metadataSectionName) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
InputStream inputstream = new FileInputStream(this.resourceIndex.getPackMcmeta());
|
||||
return (T)AbstractResourcePack.readMetadata(metadataSerializer, inputstream, metadataSectionName);
|
||||
}
|
||||
catch (RuntimeException var4)
|
||||
{
|
||||
return (T)null;
|
||||
}
|
||||
catch (FileNotFoundException var5)
|
||||
{
|
||||
return (T)null;
|
||||
}
|
||||
}
|
||||
|
||||
public BufferedImage getPackImage() throws IOException
|
||||
{
|
||||
return TextureUtil.readBufferedImage(DefaultResourcePack.class.getResourceAsStream("/" + (new ResourceLocation("pack.png")).getResourcePath()));
|
||||
}
|
||||
|
||||
public String getPackName()
|
||||
{
|
||||
return "Default";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import net.minecraft.client.resources.data.MetadataSerializer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class FallbackResourceManager implements IResourceManager
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
protected final List<IResourcePack> resourcePacks = Lists.<IResourcePack>newArrayList();
|
||||
private final MetadataSerializer frmMetadataSerializer;
|
||||
|
||||
public FallbackResourceManager(MetadataSerializer frmMetadataSerializerIn)
|
||||
{
|
||||
this.frmMetadataSerializer = frmMetadataSerializerIn;
|
||||
}
|
||||
|
||||
public void addResourcePack(IResourcePack resourcePack)
|
||||
{
|
||||
this.resourcePacks.add(resourcePack);
|
||||
}
|
||||
|
||||
public Set<String> getResourceDomains()
|
||||
{
|
||||
return Collections.<String>emptySet();
|
||||
}
|
||||
|
||||
public IResource getResource(ResourceLocation location) throws IOException
|
||||
{
|
||||
this.checkResourcePath(location);
|
||||
IResourcePack iresourcepack = null;
|
||||
ResourceLocation resourcelocation = getLocationMcmeta(location);
|
||||
|
||||
for (int i = this.resourcePacks.size() - 1; i >= 0; --i)
|
||||
{
|
||||
IResourcePack iresourcepack1 = this.resourcePacks.get(i);
|
||||
|
||||
if (iresourcepack == null && iresourcepack1.resourceExists(resourcelocation))
|
||||
{
|
||||
iresourcepack = iresourcepack1;
|
||||
}
|
||||
|
||||
if (iresourcepack1.resourceExists(location))
|
||||
{
|
||||
InputStream inputstream = null;
|
||||
|
||||
if (iresourcepack != null)
|
||||
{
|
||||
inputstream = this.getInputStream(resourcelocation, iresourcepack);
|
||||
}
|
||||
|
||||
return new SimpleResource(iresourcepack1.getPackName(), location, this.getInputStream(location, iresourcepack1), inputstream, this.frmMetadataSerializer);
|
||||
}
|
||||
}
|
||||
|
||||
throw new FileNotFoundException(location.toString());
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
protected InputStream getInputStream(ResourceLocation location, IResourcePack resourcePack) throws IOException
|
||||
{
|
||||
InputStream inputstream = resourcePack.getInputStream(location);
|
||||
return (InputStream)(LOGGER.isDebugEnabled() ? new FallbackResourceManager.InputStreamLeakedResourceLogger(inputstream, location, resourcePack.getPackName()) : inputstream);
|
||||
}
|
||||
|
||||
private void checkResourcePath(ResourceLocation p_188552_1_) throws IOException
|
||||
{
|
||||
if (p_188552_1_.getResourcePath().contains(".."))
|
||||
{
|
||||
throw new IOException("Invalid relative path to resource: " + p_188552_1_);
|
||||
}
|
||||
}
|
||||
|
||||
public List<IResource> getAllResources(ResourceLocation location) throws IOException
|
||||
{
|
||||
this.checkResourcePath(location);
|
||||
List<IResource> list = Lists.<IResource>newArrayList();
|
||||
ResourceLocation resourcelocation = getLocationMcmeta(location);
|
||||
|
||||
for (IResourcePack iresourcepack : this.resourcePacks)
|
||||
{
|
||||
if (iresourcepack.resourceExists(location))
|
||||
{
|
||||
InputStream inputstream = iresourcepack.resourceExists(resourcelocation) ? this.getInputStream(resourcelocation, iresourcepack) : null;
|
||||
list.add(new SimpleResource(iresourcepack.getPackName(), location, this.getInputStream(location, iresourcepack), inputstream, this.frmMetadataSerializer));
|
||||
}
|
||||
}
|
||||
|
||||
if (list.isEmpty())
|
||||
{
|
||||
throw new FileNotFoundException(location.toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
static ResourceLocation getLocationMcmeta(ResourceLocation location)
|
||||
{
|
||||
return new ResourceLocation(location.getResourceDomain(), location.getResourcePath() + ".mcmeta");
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
static class InputStreamLeakedResourceLogger extends InputStream
|
||||
{
|
||||
private final InputStream inputStream;
|
||||
private final String message;
|
||||
private boolean isClosed;
|
||||
|
||||
public InputStreamLeakedResourceLogger(InputStream p_i46093_1_, ResourceLocation location, String resourcePack)
|
||||
{
|
||||
this.inputStream = p_i46093_1_;
|
||||
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
|
||||
(new Exception()).printStackTrace(new PrintStream(bytearrayoutputstream));
|
||||
this.message = "Leaked resource: '" + location + "' loaded from pack: '" + resourcePack + "'\n" + bytearrayoutputstream;
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
this.inputStream.close();
|
||||
this.isClosed = true;
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable
|
||||
{
|
||||
if (!this.isClosed)
|
||||
{
|
||||
FallbackResourceManager.LOGGER.warn(this.message);
|
||||
}
|
||||
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
public int read() throws IOException
|
||||
{
|
||||
return this.inputStream.read();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class FileResourcePack extends AbstractResourcePack implements Closeable
|
||||
{
|
||||
public static final Splitter ENTRY_NAME_SPLITTER = Splitter.on('/').omitEmptyStrings().limit(3);
|
||||
private ZipFile resourcePackZipFile;
|
||||
|
||||
public FileResourcePack(File resourcePackFileIn)
|
||||
{
|
||||
super(resourcePackFileIn);
|
||||
}
|
||||
|
||||
private ZipFile getResourcePackZipFile() throws IOException
|
||||
{
|
||||
if (this.resourcePackZipFile == null)
|
||||
{
|
||||
this.resourcePackZipFile = new ZipFile(this.resourcePackFile);
|
||||
}
|
||||
|
||||
return this.resourcePackZipFile;
|
||||
}
|
||||
|
||||
protected InputStream getInputStreamByName(String name) throws IOException
|
||||
{
|
||||
ZipFile zipfile = this.getResourcePackZipFile();
|
||||
ZipEntry zipentry = zipfile.getEntry(name);
|
||||
|
||||
if (zipentry == null)
|
||||
{
|
||||
throw new ResourcePackFileNotFoundException(this.resourcePackFile, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return zipfile.getInputStream(zipentry);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasResourceName(String name)
|
||||
{
|
||||
try
|
||||
{
|
||||
return this.getResourcePackZipFile().getEntry(name) != null;
|
||||
}
|
||||
catch (IOException var3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getResourceDomains()
|
||||
{
|
||||
ZipFile zipfile;
|
||||
|
||||
try
|
||||
{
|
||||
zipfile = this.getResourcePackZipFile();
|
||||
}
|
||||
catch (IOException var8)
|
||||
{
|
||||
return Collections.<String>emptySet();
|
||||
}
|
||||
|
||||
Enumeration <? extends ZipEntry > enumeration = zipfile.entries();
|
||||
Set<String> set = Sets.<String>newHashSet();
|
||||
|
||||
while (enumeration.hasMoreElements())
|
||||
{
|
||||
ZipEntry zipentry = enumeration.nextElement();
|
||||
String s = zipentry.getName();
|
||||
|
||||
if (s.startsWith("assets/"))
|
||||
{
|
||||
List<String> list = Lists.newArrayList(ENTRY_NAME_SPLITTER.split(s));
|
||||
|
||||
if (list.size() > 1)
|
||||
{
|
||||
String s1 = list.get(1);
|
||||
|
||||
if (s1.equals(s1.toLowerCase(java.util.Locale.ROOT)))
|
||||
{
|
||||
set.add(s1);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.logNameNotLowercase(s1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable
|
||||
{
|
||||
this.close();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
if (this.resourcePackZipFile != null)
|
||||
{
|
||||
this.resourcePackZipFile.close();
|
||||
this.resourcePackZipFile = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.commons.io.filefilter.DirectoryFileFilter;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class FolderResourcePack extends AbstractResourcePack
|
||||
{
|
||||
private static final boolean ON_WINDOWS = Util.getOSType() == Util.EnumOS.WINDOWS;
|
||||
private static final CharMatcher BACKSLASH_MATCHER = CharMatcher.is('\\');
|
||||
|
||||
public FolderResourcePack(File resourcePackFileIn)
|
||||
{
|
||||
super(resourcePackFileIn);
|
||||
}
|
||||
|
||||
protected static boolean validatePath(File p_191384_0_, String p_191384_1_) throws IOException
|
||||
{
|
||||
String s = p_191384_0_.getCanonicalPath();
|
||||
|
||||
if (ON_WINDOWS)
|
||||
{
|
||||
s = BACKSLASH_MATCHER.replaceFrom(s, '/');
|
||||
}
|
||||
|
||||
return s.endsWith(p_191384_1_);
|
||||
}
|
||||
|
||||
protected InputStream getInputStreamByName(String name) throws IOException
|
||||
{
|
||||
File file1 = this.getFile(name);
|
||||
|
||||
if (file1 == null)
|
||||
{
|
||||
throw new ResourcePackFileNotFoundException(this.resourcePackFile, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new BufferedInputStream(new FileInputStream(file1));
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean hasResourceName(String name)
|
||||
{
|
||||
return this.getFile(name) != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private File getFile(String p_191385_1_)
|
||||
{
|
||||
try
|
||||
{
|
||||
File file1 = new File(this.resourcePackFile, p_191385_1_);
|
||||
|
||||
if (file1.isFile() && validatePath(file1, p_191385_1_))
|
||||
{
|
||||
return file1;
|
||||
}
|
||||
}
|
||||
catch (IOException var3)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<String> getResourceDomains()
|
||||
{
|
||||
Set<String> set = Sets.<String>newHashSet();
|
||||
File file1 = new File(this.resourcePackFile, "assets/");
|
||||
|
||||
if (file1.isDirectory())
|
||||
{
|
||||
for (File file2 : file1.listFiles((FileFilter)DirectoryFileFilter.DIRECTORY))
|
||||
{
|
||||
String s = getRelativeName(file1, file2);
|
||||
|
||||
if (s.equals(s.toLowerCase(java.util.Locale.ROOT)))
|
||||
{
|
||||
set.add(s.substring(0, s.length() - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.logNameNotLowercase(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import java.io.IOException;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.ColorizerFoliage;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class FoliageColorReloadListener implements IResourceManagerReloadListener
|
||||
{
|
||||
private static final ResourceLocation LOC_FOLIAGE_PNG = new ResourceLocation("textures/colormap/foliage.png");
|
||||
|
||||
public void onResourceManagerReload(IResourceManager resourceManager)
|
||||
{
|
||||
try
|
||||
{
|
||||
ColorizerFoliage.setFoliageBiomeColorizer(TextureUtil.readImageData(resourceManager, LOC_FOLIAGE_PNG));
|
||||
}
|
||||
catch (IOException var3)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import java.io.IOException;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.ColorizerGrass;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class GrassColorReloadListener implements IResourceManagerReloadListener
|
||||
{
|
||||
private static final ResourceLocation LOC_GRASS_PNG = new ResourceLocation("textures/colormap/grass.png");
|
||||
|
||||
public void onResourceManagerReload(IResourceManager resourceManager)
|
||||
{
|
||||
try
|
||||
{
|
||||
ColorizerGrass.setGrassBiomeColorizer(TextureUtil.readImageData(resourceManager, LOC_GRASS_PNG));
|
||||
}
|
||||
catch (IOException var3)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class I18n
|
||||
{
|
||||
private static Locale i18nLocale;
|
||||
|
||||
static void setLocale(Locale i18nLocaleIn)
|
||||
{
|
||||
i18nLocale = i18nLocaleIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the given string and then formats it. Equivilant to String.format(translate(key), parameters).
|
||||
*/
|
||||
public static String format(String translateKey, Object... parameters)
|
||||
{
|
||||
return i18nLocale.formatMessage(translateKey, parameters);
|
||||
}
|
||||
|
||||
public static boolean hasKey(String key)
|
||||
{
|
||||
return i18nLocale.hasKey(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import java.util.List;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public interface IReloadableResourceManager extends IResourceManager
|
||||
{
|
||||
void reloadResources(List<IResourcePack> resourcesPacksList);
|
||||
|
||||
void registerReloadListener(IResourceManagerReloadListener reloadListener);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.InputStream;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.client.resources.data.IMetadataSection;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public interface IResource extends Closeable
|
||||
{
|
||||
ResourceLocation getResourceLocation();
|
||||
|
||||
InputStream getInputStream();
|
||||
|
||||
boolean hasMetadata();
|
||||
|
||||
@Nullable
|
||||
<T extends IMetadataSection> T getMetadata(String sectionName);
|
||||
|
||||
String getResourcePackName();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public interface IResourceManager
|
||||
{
|
||||
Set<String> getResourceDomains();
|
||||
|
||||
IResource getResource(ResourceLocation location) throws IOException;
|
||||
|
||||
List<IResource> getAllResources(ResourceLocation location) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
/**
|
||||
* @deprecated Forge: {@link net.minecraftforge.client.resource.ISelectiveResourceReloadListener}, which selectively allows
|
||||
* individual resource types being reloaded should rather be used where possible.
|
||||
*/
|
||||
@Deprecated
|
||||
@SideOnly(Side.CLIENT)
|
||||
public interface IResourceManagerReloadListener
|
||||
{
|
||||
void onResourceManagerReload(IResourceManager resourceManager);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.client.resources.data.IMetadataSection;
|
||||
import net.minecraft.client.resources.data.MetadataSerializer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public interface IResourcePack
|
||||
{
|
||||
InputStream getInputStream(ResourceLocation location) throws IOException;
|
||||
|
||||
boolean resourceExists(ResourceLocation location);
|
||||
|
||||
Set<String> getResourceDomains();
|
||||
|
||||
@Nullable
|
||||
<T extends IMetadataSection> T getPackMetadata(MetadataSerializer metadataSerializer, String metadataSectionName) throws IOException;
|
||||
|
||||
BufferedImage getPackImage() throws IOException;
|
||||
|
||||
String getPackName();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class Language implements Comparable<Language>
|
||||
{
|
||||
private final String languageCode;
|
||||
private final String region;
|
||||
private final String name;
|
||||
private final boolean bidirectional;
|
||||
|
||||
public Language(String languageCodeIn, String regionIn, String nameIn, boolean bidirectionalIn)
|
||||
{
|
||||
this.languageCode = languageCodeIn;
|
||||
this.region = regionIn;
|
||||
this.name = nameIn;
|
||||
this.bidirectional = bidirectionalIn;
|
||||
String[] splitLangCode = languageCode.split("_", 2);
|
||||
this.javaLocale = new java.util.Locale(splitLangCode[0], splitLangCode[1]);
|
||||
}
|
||||
|
||||
public String getLanguageCode()
|
||||
{
|
||||
return this.languageCode;
|
||||
}
|
||||
|
||||
public boolean isBidirectional()
|
||||
{
|
||||
return this.bidirectional;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s (%s)", this.name, this.region);
|
||||
}
|
||||
|
||||
public boolean equals(Object p_equals_1_)
|
||||
{
|
||||
if (this == p_equals_1_)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return !(p_equals_1_ instanceof Language) ? false : this.languageCode.equals(((Language)p_equals_1_).languageCode);
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode()
|
||||
{
|
||||
return this.languageCode.hashCode();
|
||||
}
|
||||
|
||||
public int compareTo(Language p_compareTo_1_)
|
||||
{
|
||||
return this.languageCode.compareTo(p_compareTo_1_.languageCode);
|
||||
}
|
||||
|
||||
// Forge: add access to Locale so modders can create correct string and number formatters
|
||||
private final java.util.Locale javaLocale;
|
||||
public java.util.Locale getJavaLocale() { return javaLocale; }
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import net.minecraft.client.resources.data.LanguageMetadataSection;
|
||||
import net.minecraft.client.resources.data.MetadataSerializer;
|
||||
import net.minecraft.util.text.translation.LanguageMap;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class LanguageManager implements IResourceManagerReloadListener
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private final MetadataSerializer metadataSerializer;
|
||||
private String currentLanguage;
|
||||
protected static final Locale CURRENT_LOCALE = new Locale();
|
||||
private final Map<String, Language> languageMap = Maps.<String, Language>newHashMap();
|
||||
|
||||
public LanguageManager(MetadataSerializer theMetadataSerializerIn, String currentLanguageIn)
|
||||
{
|
||||
this.metadataSerializer = theMetadataSerializerIn;
|
||||
this.currentLanguage = currentLanguageIn;
|
||||
I18n.setLocale(CURRENT_LOCALE);
|
||||
}
|
||||
|
||||
public void parseLanguageMetadata(List<IResourcePack> resourcesPacks)
|
||||
{
|
||||
this.languageMap.clear();
|
||||
|
||||
for (IResourcePack iresourcepack : resourcesPacks)
|
||||
{
|
||||
try
|
||||
{
|
||||
LanguageMetadataSection languagemetadatasection = (LanguageMetadataSection)iresourcepack.getPackMetadata(this.metadataSerializer, "language");
|
||||
|
||||
if (languagemetadatasection != null)
|
||||
{
|
||||
for (Language language : languagemetadatasection.getLanguages())
|
||||
{
|
||||
if (!this.languageMap.containsKey(language.getLanguageCode()))
|
||||
{
|
||||
this.languageMap.put(language.getLanguageCode(), language);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (RuntimeException runtimeexception)
|
||||
{
|
||||
LOGGER.warn("Unable to parse language metadata section of resourcepack: {}", iresourcepack.getPackName(), runtimeexception);
|
||||
}
|
||||
catch (IOException ioexception)
|
||||
{
|
||||
LOGGER.warn("Unable to parse language metadata section of resourcepack: {}", iresourcepack.getPackName(), ioexception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onResourceManagerReload(IResourceManager resourceManager)
|
||||
{
|
||||
List<String> list = Lists.newArrayList("en_us");
|
||||
|
||||
if (!"en_us".equals(this.currentLanguage))
|
||||
{
|
||||
list.add(this.currentLanguage);
|
||||
}
|
||||
|
||||
CURRENT_LOCALE.loadLocaleDataFiles(resourceManager, list);
|
||||
LanguageMap.replaceWith(CURRENT_LOCALE.properties);
|
||||
}
|
||||
|
||||
public boolean isCurrentLocaleUnicode()
|
||||
{
|
||||
return CURRENT_LOCALE.isUnicode();
|
||||
}
|
||||
|
||||
public boolean isCurrentLanguageBidirectional()
|
||||
{
|
||||
return this.getCurrentLanguage() != null && this.getCurrentLanguage().isBidirectional();
|
||||
}
|
||||
|
||||
public void setCurrentLanguage(Language currentLanguageIn)
|
||||
{
|
||||
this.currentLanguage = currentLanguageIn.getLanguageCode();
|
||||
}
|
||||
|
||||
public Language getCurrentLanguage()
|
||||
{
|
||||
String s = this.languageMap.containsKey(this.currentLanguage) ? this.currentLanguage : "en_us";
|
||||
return this.languageMap.get(s);
|
||||
}
|
||||
|
||||
public SortedSet<Language> getLanguages()
|
||||
{
|
||||
return Sets.newTreeSet(this.languageMap.values());
|
||||
}
|
||||
|
||||
public Language getLanguage(String p_191960_1_)
|
||||
{
|
||||
return this.languageMap.get(p_191960_1_);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.client.resources.data.IMetadataSection;
|
||||
import net.minecraft.client.resources.data.MetadataSerializer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class LegacyV2Adapter implements IResourcePack
|
||||
{
|
||||
private final IResourcePack pack;
|
||||
|
||||
public LegacyV2Adapter(IResourcePack packIn)
|
||||
{
|
||||
this.pack = packIn;
|
||||
}
|
||||
|
||||
public InputStream getInputStream(ResourceLocation location) throws IOException
|
||||
{
|
||||
return this.pack.getInputStream(this.fudgePath(location));
|
||||
}
|
||||
|
||||
private ResourceLocation fudgePath(ResourceLocation p_191382_1_)
|
||||
{
|
||||
String s = p_191382_1_.getResourcePath();
|
||||
|
||||
if (!"lang/swg_de.lang".equals(s) && s.startsWith("lang/") && s.endsWith(".lang"))
|
||||
{
|
||||
int i = s.indexOf(95);
|
||||
|
||||
if (i != -1)
|
||||
{
|
||||
final String s1 = s.substring(0, i + 1) + s.substring(i + 1, s.indexOf(46, i)).toUpperCase() + ".lang";
|
||||
return new ResourceLocation(p_191382_1_.getResourceDomain(), "")
|
||||
{
|
||||
public String getResourcePath()
|
||||
{
|
||||
return s1;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return p_191382_1_;
|
||||
}
|
||||
|
||||
public boolean resourceExists(ResourceLocation location)
|
||||
{
|
||||
return this.pack.resourceExists(this.fudgePath(location));
|
||||
}
|
||||
|
||||
public Set<String> getResourceDomains()
|
||||
{
|
||||
return this.pack.getResourceDomains();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T extends IMetadataSection> T getPackMetadata(MetadataSerializer metadataSerializer, String metadataSectionName) throws IOException
|
||||
{
|
||||
return (T)this.pack.getPackMetadata(metadataSerializer, metadataSectionName);
|
||||
}
|
||||
|
||||
public BufferedImage getPackImage() throws IOException
|
||||
{
|
||||
return this.pack.getPackImage();
|
||||
}
|
||||
|
||||
public String getPackName()
|
||||
{
|
||||
return this.pack.getPackName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.IllegalFormatException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class Locale
|
||||
{
|
||||
/** Splits on "=" */
|
||||
private static final Splitter SPLITTER = Splitter.on('=').limit(2);
|
||||
private static final Pattern PATTERN = Pattern.compile("%(\\d+\\$)?[\\d\\.]*[df]");
|
||||
Map<String, String> properties = Maps.<String, String>newHashMap();
|
||||
private boolean unicode;
|
||||
|
||||
/**
|
||||
* For each domain $D and language $L, attempts to load the resource $D:lang/$L.lang
|
||||
*/
|
||||
public synchronized void loadLocaleDataFiles(IResourceManager resourceManager, List<String> languageList)
|
||||
{
|
||||
this.properties.clear();
|
||||
|
||||
for (String s : languageList)
|
||||
{
|
||||
String s1 = String.format("lang/%s.lang", s);
|
||||
|
||||
for (String s2 : resourceManager.getResourceDomains())
|
||||
{
|
||||
try
|
||||
{
|
||||
this.loadLocaleData(resourceManager.getAllResources(new ResourceLocation(s2, s1)));
|
||||
}
|
||||
catch (IOException var9)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.checkUnicode();
|
||||
}
|
||||
|
||||
public boolean isUnicode()
|
||||
{
|
||||
return this.unicode;
|
||||
}
|
||||
|
||||
private void checkUnicode()
|
||||
{
|
||||
this.unicode = false;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
for (String s : this.properties.values())
|
||||
{
|
||||
int k = s.length();
|
||||
j += k;
|
||||
|
||||
for (int l = 0; l < k; ++l)
|
||||
{
|
||||
if (s.charAt(l) >= 256)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float f = (float)i / (float)j;
|
||||
this.unicode = (double)f > 0.1D;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the locale data for the list of resources.
|
||||
*/
|
||||
private void loadLocaleData(List<IResource> resourcesList) throws IOException
|
||||
{
|
||||
for (IResource iresource : resourcesList)
|
||||
{
|
||||
InputStream inputstream = iresource.getInputStream();
|
||||
|
||||
try
|
||||
{
|
||||
this.loadLocaleData(inputstream);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly(inputstream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadLocaleData(InputStream inputStreamIn) throws IOException
|
||||
{
|
||||
inputStreamIn = net.minecraftforge.fml.common.FMLCommonHandler.instance().loadLanguage(properties, inputStreamIn);
|
||||
if (inputStreamIn == null) return;
|
||||
for (String s : IOUtils.readLines(inputStreamIn, StandardCharsets.UTF_8))
|
||||
{
|
||||
if (!s.isEmpty() && s.charAt(0) != '#')
|
||||
{
|
||||
String[] astring = (String[])Iterables.toArray(SPLITTER.split(s), String.class);
|
||||
|
||||
if (astring != null && astring.length == 2)
|
||||
{
|
||||
String s1 = astring[0];
|
||||
String s2 = PATTERN.matcher(astring[1]).replaceAll("%$1s");
|
||||
this.properties.put(s1, s2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation, or the key itself if the key could not be translated.
|
||||
*/
|
||||
private String translateKeyPrivate(String translateKey)
|
||||
{
|
||||
String s = this.properties.get(translateKey);
|
||||
return s == null ? translateKey : s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls String.format(translateKey(key), params)
|
||||
*/
|
||||
public String formatMessage(String translateKey, Object[] parameters)
|
||||
{
|
||||
String s = this.translateKeyPrivate(translateKey);
|
||||
|
||||
try
|
||||
{
|
||||
return String.format(s, parameters);
|
||||
}
|
||||
catch (IllegalFormatException var5)
|
||||
{
|
||||
return "Format error: " + s;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasKey(String key)
|
||||
{
|
||||
return this.properties.containsKey(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ResourceIndex
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private final Map<String, File> resourceMap = Maps.<String, File>newHashMap();
|
||||
|
||||
protected ResourceIndex()
|
||||
{
|
||||
}
|
||||
|
||||
public ResourceIndex(File assetsFolder, String indexName)
|
||||
{
|
||||
File file1 = new File(assetsFolder, "objects");
|
||||
File file2 = new File(assetsFolder, "indexes/" + indexName + ".json");
|
||||
BufferedReader bufferedreader = null;
|
||||
|
||||
try
|
||||
{
|
||||
bufferedreader = Files.newReader(file2, StandardCharsets.UTF_8);
|
||||
JsonObject jsonobject = (new JsonParser()).parse(bufferedreader).getAsJsonObject();
|
||||
JsonObject jsonobject1 = JsonUtils.getJsonObject(jsonobject, "objects", (JsonObject)null);
|
||||
|
||||
if (jsonobject1 != null)
|
||||
{
|
||||
for (Entry<String, JsonElement> entry : jsonobject1.entrySet())
|
||||
{
|
||||
JsonObject jsonobject2 = (JsonObject)entry.getValue();
|
||||
String s = entry.getKey();
|
||||
String[] astring = s.split("/", 2);
|
||||
String s1 = astring.length == 1 ? astring[0] : astring[0] + ":" + astring[1];
|
||||
String s2 = JsonUtils.getString(jsonobject2, "hash");
|
||||
File file3 = new File(file1, s2.substring(0, 2) + "/" + s2);
|
||||
this.resourceMap.put(s1, file3);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JsonParseException var20)
|
||||
{
|
||||
LOGGER.error("Unable to parse resource index file: {}", (Object)file2);
|
||||
}
|
||||
catch (FileNotFoundException var21)
|
||||
{
|
||||
LOGGER.error("Can't find the resource index file: {}", (Object)file2);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly((Reader)bufferedreader);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public File getFile(ResourceLocation location)
|
||||
{
|
||||
String s = location.toString();
|
||||
return this.resourceMap.get(s);
|
||||
}
|
||||
|
||||
public boolean isFileExisting(ResourceLocation location)
|
||||
{
|
||||
File file1 = this.getFile(location);
|
||||
return file1 != null && file1.isFile();
|
||||
}
|
||||
|
||||
public File getPackMcmeta()
|
||||
{
|
||||
return this.resourceMap.get("pack.mcmeta");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import java.io.File;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ResourceIndexFolder extends ResourceIndex
|
||||
{
|
||||
private final File baseDir;
|
||||
|
||||
public ResourceIndexFolder(File folder)
|
||||
{
|
||||
this.baseDir = folder;
|
||||
}
|
||||
|
||||
public File getFile(ResourceLocation location)
|
||||
{
|
||||
return new File(this.baseDir, location.toString().replace(':', '/'));
|
||||
}
|
||||
|
||||
public File getPackMcmeta()
|
||||
{
|
||||
return new File(this.baseDir, "pack.mcmeta");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ResourcePackFileNotFoundException extends FileNotFoundException
|
||||
{
|
||||
public ResourcePackFileNotFoundException(File resourcePack, String fileName)
|
||||
{
|
||||
super(String.format("'%s' in ResourcePack '%s'", fileName, resourcePack));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import java.util.List;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.GuiListExtended;
|
||||
import net.minecraft.client.gui.GuiScreenResourcePacks;
|
||||
import net.minecraft.client.gui.GuiYesNo;
|
||||
import net.minecraft.client.gui.GuiYesNoCallback;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public abstract class ResourcePackListEntry implements GuiListExtended.IGuiListEntry
|
||||
{
|
||||
private static final ResourceLocation RESOURCE_PACKS_TEXTURE = new ResourceLocation("textures/gui/resource_packs.png");
|
||||
private static final ITextComponent INCOMPATIBLE = new TextComponentTranslation("resourcePack.incompatible", new Object[0]);
|
||||
private static final ITextComponent INCOMPATIBLE_OLD = new TextComponentTranslation("resourcePack.incompatible.old", new Object[0]);
|
||||
private static final ITextComponent INCOMPATIBLE_NEW = new TextComponentTranslation("resourcePack.incompatible.new", new Object[0]);
|
||||
protected final Minecraft mc;
|
||||
protected final GuiScreenResourcePacks resourcePacksGUI;
|
||||
|
||||
public ResourcePackListEntry(GuiScreenResourcePacks resourcePacksGUIIn)
|
||||
{
|
||||
this.resourcePacksGUI = resourcePacksGUIIn;
|
||||
this.mc = Minecraft.getMinecraft();
|
||||
}
|
||||
|
||||
public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, int mouseX, int mouseY, boolean isSelected, float partialTicks)
|
||||
{
|
||||
int i = this.getResourcePackFormat();
|
||||
|
||||
if (i != 3)
|
||||
{
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
Gui.drawRect(x - 1, y - 1, x + listWidth - 9, y + slotHeight + 1, -8978432);
|
||||
}
|
||||
|
||||
this.bindResourcePackIcon();
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
Gui.drawModalRectWithCustomSizedTexture(x, y, 0.0F, 0.0F, 32, 32, 32.0F, 32.0F);
|
||||
String s = this.getResourcePackName();
|
||||
String s1 = this.getResourcePackDescription();
|
||||
|
||||
if (this.showHoverOverlay() && (this.mc.gameSettings.touchscreen || isSelected))
|
||||
{
|
||||
this.mc.getTextureManager().bindTexture(RESOURCE_PACKS_TEXTURE);
|
||||
Gui.drawRect(x, y, x + 32, y + 32, -1601138544);
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
int j = mouseX - x;
|
||||
int k = mouseY - y;
|
||||
|
||||
if (i < 3)
|
||||
{
|
||||
s = INCOMPATIBLE.getFormattedText();
|
||||
s1 = INCOMPATIBLE_OLD.getFormattedText();
|
||||
}
|
||||
else if (i > 3)
|
||||
{
|
||||
s = INCOMPATIBLE.getFormattedText();
|
||||
s1 = INCOMPATIBLE_NEW.getFormattedText();
|
||||
}
|
||||
|
||||
if (this.canMoveRight())
|
||||
{
|
||||
if (j < 32)
|
||||
{
|
||||
Gui.drawModalRectWithCustomSizedTexture(x, y, 0.0F, 32.0F, 32, 32, 256.0F, 256.0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
Gui.drawModalRectWithCustomSizedTexture(x, y, 0.0F, 0.0F, 32, 32, 256.0F, 256.0F);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.canMoveLeft())
|
||||
{
|
||||
if (j < 16)
|
||||
{
|
||||
Gui.drawModalRectWithCustomSizedTexture(x, y, 32.0F, 32.0F, 32, 32, 256.0F, 256.0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
Gui.drawModalRectWithCustomSizedTexture(x, y, 32.0F, 0.0F, 32, 32, 256.0F, 256.0F);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.canMoveUp())
|
||||
{
|
||||
if (j < 32 && j > 16 && k < 16)
|
||||
{
|
||||
Gui.drawModalRectWithCustomSizedTexture(x, y, 96.0F, 32.0F, 32, 32, 256.0F, 256.0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
Gui.drawModalRectWithCustomSizedTexture(x, y, 96.0F, 0.0F, 32, 32, 256.0F, 256.0F);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.canMoveDown())
|
||||
{
|
||||
if (j < 32 && j > 16 && k > 16)
|
||||
{
|
||||
Gui.drawModalRectWithCustomSizedTexture(x, y, 64.0F, 32.0F, 32, 32, 256.0F, 256.0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
Gui.drawModalRectWithCustomSizedTexture(x, y, 64.0F, 0.0F, 32, 32, 256.0F, 256.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int i1 = this.mc.fontRenderer.getStringWidth(s);
|
||||
|
||||
if (i1 > 157)
|
||||
{
|
||||
s = this.mc.fontRenderer.trimStringToWidth(s, 157 - this.mc.fontRenderer.getStringWidth("...")) + "...";
|
||||
}
|
||||
|
||||
this.mc.fontRenderer.drawStringWithShadow(s, (float)(x + 32 + 2), (float)(y + 1), 16777215);
|
||||
List<String> list = this.mc.fontRenderer.listFormattedStringToWidth(s1, 157);
|
||||
|
||||
for (int l = 0; l < 2 && l < list.size(); ++l)
|
||||
{
|
||||
this.mc.fontRenderer.drawStringWithShadow(list.get(l), (float)(x + 32 + 2), (float)(y + 12 + 10 * l), 8421504);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract int getResourcePackFormat();
|
||||
|
||||
protected abstract String getResourcePackDescription();
|
||||
|
||||
protected abstract String getResourcePackName();
|
||||
|
||||
protected abstract void bindResourcePackIcon();
|
||||
|
||||
protected boolean showHoverOverlay()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean canMoveRight()
|
||||
{
|
||||
return !this.resourcePacksGUI.hasResourcePackEntry(this);
|
||||
}
|
||||
|
||||
protected boolean canMoveLeft()
|
||||
{
|
||||
return this.resourcePacksGUI.hasResourcePackEntry(this);
|
||||
}
|
||||
|
||||
protected boolean canMoveUp()
|
||||
{
|
||||
List<ResourcePackListEntry> list = this.resourcePacksGUI.getListContaining(this);
|
||||
int i = list.indexOf(this);
|
||||
return i > 0 && ((ResourcePackListEntry)list.get(i - 1)).showHoverOverlay();
|
||||
}
|
||||
|
||||
protected boolean canMoveDown()
|
||||
{
|
||||
List<ResourcePackListEntry> list = this.resourcePacksGUI.getListContaining(this);
|
||||
int i = list.indexOf(this);
|
||||
return i >= 0 && i < list.size() - 1 && ((ResourcePackListEntry)list.get(i + 1)).showHoverOverlay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the mouse is clicked within this entry. Returning true means that something within this entry was
|
||||
* clicked and the list should not be dragged.
|
||||
*/
|
||||
public boolean mousePressed(int slotIndex, int mouseX, int mouseY, int mouseEvent, int relativeX, int relativeY)
|
||||
{
|
||||
if (this.showHoverOverlay() && relativeX <= 32)
|
||||
{
|
||||
if (this.canMoveRight())
|
||||
{
|
||||
this.resourcePacksGUI.markChanged();
|
||||
final int j = ((ResourcePackListEntry)this.resourcePacksGUI.getSelectedResourcePacks().get(0)).isServerPack() ? 1 : 0;
|
||||
int l = this.getResourcePackFormat();
|
||||
|
||||
if (l == 3)
|
||||
{
|
||||
this.resourcePacksGUI.getListContaining(this).remove(this);
|
||||
this.resourcePacksGUI.getSelectedResourcePacks().add(j, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
String s = I18n.format("resourcePack.incompatible.confirm.title");
|
||||
String s1 = I18n.format("resourcePack.incompatible.confirm." + (l > 3 ? "new" : "old"));
|
||||
this.mc.displayGuiScreen(new GuiYesNo(new GuiYesNoCallback()
|
||||
{
|
||||
public void confirmClicked(boolean result, int id)
|
||||
{
|
||||
List<ResourcePackListEntry> list2 = ResourcePackListEntry.this.resourcePacksGUI.getListContaining(ResourcePackListEntry.this);
|
||||
ResourcePackListEntry.this.mc.displayGuiScreen(ResourcePackListEntry.this.resourcePacksGUI);
|
||||
|
||||
if (result)
|
||||
{
|
||||
list2.remove(ResourcePackListEntry.this);
|
||||
ResourcePackListEntry.this.resourcePacksGUI.getSelectedResourcePacks().add(j, ResourcePackListEntry.this);
|
||||
}
|
||||
}
|
||||
}, s, s1, 0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (relativeX < 16 && this.canMoveLeft())
|
||||
{
|
||||
this.resourcePacksGUI.getListContaining(this).remove(this);
|
||||
this.resourcePacksGUI.getAvailableResourcePacks().add(0, this);
|
||||
this.resourcePacksGUI.markChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (relativeX > 16 && relativeY < 16 && this.canMoveUp())
|
||||
{
|
||||
List<ResourcePackListEntry> list1 = this.resourcePacksGUI.getListContaining(this);
|
||||
int k = list1.indexOf(this);
|
||||
list1.remove(this);
|
||||
list1.add(k - 1, this);
|
||||
this.resourcePacksGUI.markChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (relativeX > 16 && relativeY > 16 && this.canMoveDown())
|
||||
{
|
||||
List<ResourcePackListEntry> list = this.resourcePacksGUI.getListContaining(this);
|
||||
int i = list.indexOf(this);
|
||||
list.remove(this);
|
||||
list.add(i + 1, this);
|
||||
this.resourcePacksGUI.markChanged();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void updatePosition(int slotIndex, int x, int y, float partialTicks)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when the mouse button is released. Arguments: index, x, y, mouseEvent, relativeX, relativeY
|
||||
*/
|
||||
public void mouseReleased(int slotIndex, int x, int y, int mouseEvent, int relativeX, int relativeY)
|
||||
{
|
||||
}
|
||||
|
||||
public boolean isServerPack()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreenResourcePacks;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ResourcePackListEntryDefault extends ResourcePackListEntryServer
|
||||
{
|
||||
public ResourcePackListEntryDefault(GuiScreenResourcePacks resourcePacksGUIIn)
|
||||
{
|
||||
super(resourcePacksGUIIn, Minecraft.getMinecraft().getResourcePackRepository().rprDefaultResourcePack);
|
||||
}
|
||||
|
||||
protected String getResourcePackName()
|
||||
{
|
||||
return "Default";
|
||||
}
|
||||
|
||||
public boolean isServerPack()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import net.minecraft.client.gui.GuiScreenResourcePacks;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ResourcePackListEntryFound extends ResourcePackListEntry
|
||||
{
|
||||
private final ResourcePackRepository.Entry resourcePackEntry;
|
||||
|
||||
public ResourcePackListEntryFound(GuiScreenResourcePacks resourcePacksGUIIn, ResourcePackRepository.Entry entry)
|
||||
{
|
||||
super(resourcePacksGUIIn);
|
||||
this.resourcePackEntry = entry;
|
||||
}
|
||||
|
||||
protected void bindResourcePackIcon()
|
||||
{
|
||||
this.resourcePackEntry.bindTexturePackIcon(this.mc.getTextureManager());
|
||||
}
|
||||
|
||||
protected int getResourcePackFormat()
|
||||
{
|
||||
return this.resourcePackEntry.getPackFormat();
|
||||
}
|
||||
|
||||
protected String getResourcePackDescription()
|
||||
{
|
||||
return this.resourcePackEntry.getTexturePackDescription();
|
||||
}
|
||||
|
||||
protected String getResourcePackName()
|
||||
{
|
||||
return this.resourcePackEntry.getResourcePackName();
|
||||
}
|
||||
|
||||
public ResourcePackRepository.Entry getResourcePackEntry()
|
||||
{
|
||||
return this.resourcePackEntry;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import java.io.IOException;
|
||||
import net.minecraft.client.gui.GuiScreenResourcePacks;
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.client.resources.data.PackMetadataSection;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ResourcePackListEntryServer extends ResourcePackListEntry
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private final IResourcePack resourcePack;
|
||||
private final ResourceLocation resourcePackIcon;
|
||||
|
||||
public ResourcePackListEntryServer(GuiScreenResourcePacks resourcePacksGUIIn, IResourcePack resourcePackIn)
|
||||
{
|
||||
super(resourcePacksGUIIn);
|
||||
this.resourcePack = resourcePackIn;
|
||||
DynamicTexture dynamictexture;
|
||||
|
||||
try
|
||||
{
|
||||
dynamictexture = new DynamicTexture(resourcePackIn.getPackImage());
|
||||
}
|
||||
catch (IOException var5)
|
||||
{
|
||||
dynamictexture = TextureUtil.MISSING_TEXTURE;
|
||||
}
|
||||
|
||||
this.resourcePackIcon = this.mc.getTextureManager().getDynamicTextureLocation("texturepackicon", dynamictexture);
|
||||
}
|
||||
|
||||
protected int getResourcePackFormat()
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
protected String getResourcePackDescription()
|
||||
{
|
||||
try
|
||||
{
|
||||
PackMetadataSection packmetadatasection = (PackMetadataSection)this.resourcePack.getPackMetadata(this.mc.getResourcePackRepository().rprMetadataSerializer, "pack");
|
||||
|
||||
if (packmetadatasection != null)
|
||||
{
|
||||
return packmetadatasection.getPackDescription().getFormattedText();
|
||||
}
|
||||
}
|
||||
catch (JsonParseException jsonparseexception)
|
||||
{
|
||||
LOGGER.error("Couldn't load metadata info", (Throwable)jsonparseexception);
|
||||
}
|
||||
catch (IOException ioexception)
|
||||
{
|
||||
LOGGER.error("Couldn't load metadata info", (Throwable)ioexception);
|
||||
}
|
||||
|
||||
return TextFormatting.RED + "Missing " + "pack.mcmeta" + " :(";
|
||||
}
|
||||
|
||||
protected boolean canMoveRight()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean canMoveLeft()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean canMoveUp()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean canMoveDown()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected String getResourcePackName()
|
||||
{
|
||||
return "Server";
|
||||
}
|
||||
|
||||
protected void bindResourcePackIcon()
|
||||
{
|
||||
this.mc.getTextureManager().bindTexture(this.resourcePackIcon);
|
||||
}
|
||||
|
||||
protected boolean showHoverOverlay()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isServerPack()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,535 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreenWorking;
|
||||
import net.minecraft.client.renderer.texture.DynamicTexture;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.renderer.texture.TextureUtil;
|
||||
import net.minecraft.client.resources.data.MetadataSerializer;
|
||||
import net.minecraft.client.resources.data.PackMetadataSection;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.util.HttpUtil;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.comparator.LastModifiedFileComparator;
|
||||
import org.apache.commons.io.filefilter.IOFileFilter;
|
||||
import org.apache.commons.io.filefilter.TrueFileFilter;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ResourcePackRepository
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final FileFilter RESOURCE_PACK_FILTER = new FileFilter()
|
||||
{
|
||||
public boolean accept(File p_accept_1_)
|
||||
{
|
||||
boolean flag = p_accept_1_.isFile() && p_accept_1_.getName().endsWith(".zip");
|
||||
boolean flag1 = p_accept_1_.isDirectory() && (new File(p_accept_1_, "pack.mcmeta")).isFile();
|
||||
return flag || flag1;
|
||||
}
|
||||
};
|
||||
private static final Pattern SHA1 = Pattern.compile("^[a-fA-F0-9]{40}$");
|
||||
private static final ResourceLocation UNKNOWN_PACK_TEXTURE = new ResourceLocation("textures/misc/unknown_pack.png");
|
||||
private final File dirResourcepacks;
|
||||
public final IResourcePack rprDefaultResourcePack;
|
||||
private final File dirServerResourcepacks;
|
||||
public final MetadataSerializer rprMetadataSerializer;
|
||||
private IResourcePack serverResourcePack;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
/** ResourcesPack currently beeing downloaded */
|
||||
private ListenableFuture<Object> downloadingPacks;
|
||||
private List<ResourcePackRepository.Entry> repositoryEntriesAll = Lists.<ResourcePackRepository.Entry>newArrayList();
|
||||
private final List<ResourcePackRepository.Entry> repositoryEntries = Lists.<ResourcePackRepository.Entry>newArrayList();
|
||||
|
||||
public ResourcePackRepository(File dirResourcepacksIn, File dirServerResourcepacksIn, IResourcePack rprDefaultResourcePackIn, MetadataSerializer rprMetadataSerializerIn, GameSettings settings)
|
||||
{
|
||||
this.dirResourcepacks = dirResourcepacksIn;
|
||||
this.dirServerResourcepacks = dirServerResourcepacksIn;
|
||||
this.rprDefaultResourcePack = rprDefaultResourcePackIn;
|
||||
this.rprMetadataSerializer = rprMetadataSerializerIn;
|
||||
this.fixDirResourcepacks();
|
||||
this.updateRepositoryEntriesAll();
|
||||
Iterator<String> iterator = settings.resourcePacks.iterator();
|
||||
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
String s = iterator.next();
|
||||
|
||||
for (ResourcePackRepository.Entry resourcepackrepository$entry : this.repositoryEntriesAll)
|
||||
{
|
||||
if (resourcepackrepository$entry.getResourcePackName().equals(s))
|
||||
{
|
||||
if (resourcepackrepository$entry.getPackFormat() == 3 || settings.incompatibleResourcePacks.contains(resourcepackrepository$entry.getResourcePackName()))
|
||||
{
|
||||
this.repositoryEntries.add(resourcepackrepository$entry);
|
||||
break;
|
||||
}
|
||||
|
||||
iterator.remove();
|
||||
LOGGER.warn("Removed selected resource pack {} because it's no longer compatible", (Object)resourcepackrepository$entry.getResourcePackName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, String> getDownloadHeaders()
|
||||
{
|
||||
Map<String, String> map = Maps.<String, String>newHashMap();
|
||||
map.put("X-Minecraft-Username", Minecraft.getMinecraft().getSession().getUsername());
|
||||
map.put("X-Minecraft-UUID", Minecraft.getMinecraft().getSession().getPlayerID());
|
||||
map.put("X-Minecraft-Version", "1.12.2");
|
||||
return map;
|
||||
}
|
||||
|
||||
private void fixDirResourcepacks()
|
||||
{
|
||||
if (this.dirResourcepacks.exists())
|
||||
{
|
||||
if (!this.dirResourcepacks.isDirectory() && (!this.dirResourcepacks.delete() || !this.dirResourcepacks.mkdirs()))
|
||||
{
|
||||
LOGGER.warn("Unable to recreate resourcepack folder, it exists but is not a directory: {}", (Object)this.dirResourcepacks);
|
||||
}
|
||||
}
|
||||
else if (!this.dirResourcepacks.mkdirs())
|
||||
{
|
||||
LOGGER.warn("Unable to create resourcepack folder: {}", (Object)this.dirResourcepacks);
|
||||
}
|
||||
}
|
||||
|
||||
private List<File> getResourcePackFiles()
|
||||
{
|
||||
return this.dirResourcepacks.isDirectory() ? Arrays.asList(this.dirResourcepacks.listFiles(RESOURCE_PACK_FILTER)) : Collections.emptyList();
|
||||
}
|
||||
|
||||
private IResourcePack getResourcePack(File p_191399_1_)
|
||||
{
|
||||
IResourcePack iresourcepack;
|
||||
|
||||
if (p_191399_1_.isDirectory())
|
||||
{
|
||||
iresourcepack = new FolderResourcePack(p_191399_1_);
|
||||
}
|
||||
else
|
||||
{
|
||||
iresourcepack = new FileResourcePack(p_191399_1_);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
PackMetadataSection packmetadatasection = (PackMetadataSection)iresourcepack.getPackMetadata(this.rprMetadataSerializer, "pack");
|
||||
|
||||
if (packmetadatasection != null && packmetadatasection.getPackFormat() == 2)
|
||||
{
|
||||
return new LegacyV2Adapter(iresourcepack);
|
||||
}
|
||||
}
|
||||
catch (Exception var4)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
return iresourcepack;
|
||||
}
|
||||
|
||||
public void updateRepositoryEntriesAll()
|
||||
{
|
||||
List<ResourcePackRepository.Entry> list = Lists.<ResourcePackRepository.Entry>newArrayList();
|
||||
|
||||
for (File file1 : this.getResourcePackFiles())
|
||||
{
|
||||
ResourcePackRepository.Entry resourcepackrepository$entry = new ResourcePackRepository.Entry(file1);
|
||||
|
||||
if (this.repositoryEntriesAll.contains(resourcepackrepository$entry))
|
||||
{
|
||||
int i = this.repositoryEntriesAll.indexOf(resourcepackrepository$entry);
|
||||
|
||||
if (i > -1 && i < this.repositoryEntriesAll.size())
|
||||
{
|
||||
list.add(this.repositoryEntriesAll.get(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
resourcepackrepository$entry.updateResourcePack();
|
||||
list.add(resourcepackrepository$entry);
|
||||
}
|
||||
catch (Exception var6)
|
||||
{
|
||||
list.remove(resourcepackrepository$entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.repositoryEntriesAll.removeAll(list);
|
||||
|
||||
for (ResourcePackRepository.Entry resourcepackrepository$entry1 : this.repositoryEntriesAll)
|
||||
{
|
||||
resourcepackrepository$entry1.closeResourcePack();
|
||||
}
|
||||
|
||||
this.repositoryEntriesAll = list;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ResourcePackRepository.Entry getResourcePackEntry()
|
||||
{
|
||||
if (this.serverResourcePack != null)
|
||||
{
|
||||
ResourcePackRepository.Entry resourcepackrepository$entry = new ResourcePackRepository.Entry(this.serverResourcePack);
|
||||
|
||||
try
|
||||
{
|
||||
resourcepackrepository$entry.updateResourcePack();
|
||||
return resourcepackrepository$entry;
|
||||
}
|
||||
catch (IOException var3)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ResourcePackRepository.Entry> getRepositoryEntriesAll()
|
||||
{
|
||||
return ImmutableList.copyOf(this.repositoryEntriesAll);
|
||||
}
|
||||
|
||||
public List<ResourcePackRepository.Entry> getRepositoryEntries()
|
||||
{
|
||||
return ImmutableList.copyOf(this.repositoryEntries);
|
||||
}
|
||||
|
||||
public void setRepositories(List<ResourcePackRepository.Entry> repositories)
|
||||
{
|
||||
this.repositoryEntries.clear();
|
||||
this.repositoryEntries.addAll(repositories);
|
||||
}
|
||||
|
||||
public File getDirResourcepacks()
|
||||
{
|
||||
return this.dirResourcepacks;
|
||||
}
|
||||
|
||||
public ListenableFuture<Object> downloadResourcePack(String url, String hash)
|
||||
{
|
||||
String s = DigestUtils.sha1Hex(url);
|
||||
final String s1 = SHA1.matcher(hash).matches() ? hash : "";
|
||||
final File file1 = new File(this.dirServerResourcepacks, s);
|
||||
this.lock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
this.clearResourcePack();
|
||||
|
||||
if (file1.exists())
|
||||
{
|
||||
if (this.checkHash(s1, file1))
|
||||
{
|
||||
ListenableFuture listenablefuture1 = this.setServerResourcePack(file1);
|
||||
return listenablefuture1;
|
||||
}
|
||||
|
||||
LOGGER.warn("Deleting file {}", (Object)file1);
|
||||
FileUtils.deleteQuietly(file1);
|
||||
}
|
||||
|
||||
this.deleteOldServerResourcesPacks();
|
||||
final GuiScreenWorking guiscreenworking = new GuiScreenWorking();
|
||||
Map<String, String> map = getDownloadHeaders();
|
||||
final Minecraft minecraft = Minecraft.getMinecraft();
|
||||
Futures.getUnchecked(minecraft.addScheduledTask(new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
minecraft.displayGuiScreen(guiscreenworking);
|
||||
}
|
||||
}));
|
||||
final SettableFuture<Object> settablefuture = SettableFuture.<Object>create();
|
||||
this.downloadingPacks = HttpUtil.downloadResourcePack(file1, url, map, 52428800, guiscreenworking, minecraft.getProxy());
|
||||
Futures.addCallback(this.downloadingPacks, new FutureCallback<Object>()
|
||||
{
|
||||
public void onSuccess(@Nullable Object p_onSuccess_1_)
|
||||
{
|
||||
if (ResourcePackRepository.this.checkHash(s1, file1))
|
||||
{
|
||||
ResourcePackRepository.this.setServerResourcePack(file1);
|
||||
settablefuture.set((Object)null);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResourcePackRepository.LOGGER.warn("Deleting file {}", (Object)file1);
|
||||
FileUtils.deleteQuietly(file1);
|
||||
}
|
||||
}
|
||||
public void onFailure(Throwable p_onFailure_1_)
|
||||
{
|
||||
FileUtils.deleteQuietly(file1);
|
||||
settablefuture.setException(p_onFailure_1_);
|
||||
}
|
||||
});
|
||||
ListenableFuture listenablefuture = this.downloadingPacks;
|
||||
return listenablefuture;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkHash(String p_190113_1_, File p_190113_2_)
|
||||
{
|
||||
try
|
||||
{
|
||||
String s = DigestUtils.sha1Hex((InputStream)(new FileInputStream(p_190113_2_)));
|
||||
|
||||
if (p_190113_1_.isEmpty())
|
||||
{
|
||||
LOGGER.info("Found file {} without verification hash", (Object)p_190113_2_);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (s.toLowerCase(java.util.Locale.ROOT).equals(p_190113_1_.toLowerCase(java.util.Locale.ROOT)))
|
||||
{
|
||||
LOGGER.info("Found file {} matching requested hash {}", p_190113_2_, p_190113_1_);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOGGER.warn("File {} had wrong hash (expected {}, found {}).", p_190113_2_, p_190113_1_, s);
|
||||
}
|
||||
catch (IOException ioexception)
|
||||
{
|
||||
LOGGER.warn("File {} couldn't be hashed.", p_190113_2_, ioexception);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean validatePack(File p_190112_1_)
|
||||
{
|
||||
ResourcePackRepository.Entry resourcepackrepository$entry = new ResourcePackRepository.Entry(p_190112_1_);
|
||||
|
||||
try
|
||||
{
|
||||
resourcepackrepository$entry.updateResourcePack();
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
LOGGER.warn("Server resourcepack is invalid, ignoring it", (Throwable)exception);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep only the 10 most recent resources packs, delete the others
|
||||
*/
|
||||
private void deleteOldServerResourcesPacks()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<File> list = Lists.newArrayList(FileUtils.listFiles(this.dirServerResourcepacks, TrueFileFilter.TRUE, (IOFileFilter)null));
|
||||
Collections.sort(list, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
|
||||
int i = 0;
|
||||
|
||||
for (File file1 : list)
|
||||
{
|
||||
if (i++ >= 10)
|
||||
{
|
||||
LOGGER.info("Deleting old server resource pack {}", (Object)file1.getName());
|
||||
FileUtils.deleteQuietly(file1);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException illegalargumentexception)
|
||||
{
|
||||
LOGGER.error("Error while deleting old server resource pack : {}", (Object)illegalargumentexception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ListenableFuture<Object> setServerResourcePack(File resourceFile)
|
||||
{
|
||||
if (!this.validatePack(resourceFile))
|
||||
{
|
||||
return Futures.<Object>immediateFailedFuture(new RuntimeException("Invalid resourcepack"));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.serverResourcePack = new FileResourcePack(resourceFile);
|
||||
return Minecraft.getMinecraft().scheduleResourcesRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the IResourcePack instance associated with this ResourcePackRepository
|
||||
*/
|
||||
@Nullable
|
||||
public IResourcePack getServerResourcePack()
|
||||
{
|
||||
return this.serverResourcePack;
|
||||
}
|
||||
|
||||
public void clearResourcePack()
|
||||
{
|
||||
this.lock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
if (this.downloadingPacks != null)
|
||||
{
|
||||
this.downloadingPacks.cancel(true);
|
||||
}
|
||||
|
||||
this.downloadingPacks = null;
|
||||
|
||||
if (this.serverResourcePack != null)
|
||||
{
|
||||
this.serverResourcePack = null;
|
||||
Minecraft.getMinecraft().scheduleResourcesRefresh();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class Entry
|
||||
{
|
||||
private final IResourcePack reResourcePack;
|
||||
private PackMetadataSection rePackMetadataSection;
|
||||
private ResourceLocation locationTexturePackIcon;
|
||||
|
||||
private Entry(File resourcePackFileIn)
|
||||
{
|
||||
this(ResourcePackRepository.this.getResourcePack(resourcePackFileIn));
|
||||
}
|
||||
|
||||
private Entry(IResourcePack reResourcePackIn)
|
||||
{
|
||||
this.reResourcePack = reResourcePackIn;
|
||||
}
|
||||
|
||||
public void updateResourcePack() throws IOException
|
||||
{
|
||||
this.rePackMetadataSection = (PackMetadataSection)this.reResourcePack.getPackMetadata(ResourcePackRepository.this.rprMetadataSerializer, "pack");
|
||||
this.closeResourcePack();
|
||||
}
|
||||
|
||||
public void bindTexturePackIcon(TextureManager textureManagerIn)
|
||||
{
|
||||
BufferedImage bufferedimage = null;
|
||||
|
||||
try
|
||||
{
|
||||
bufferedimage = this.reResourcePack.getPackImage();
|
||||
}
|
||||
catch (IOException var5)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
if (bufferedimage == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
bufferedimage = TextureUtil.readBufferedImage(Minecraft.getMinecraft().getResourceManager().getResource(ResourcePackRepository.UNKNOWN_PACK_TEXTURE).getInputStream());
|
||||
}
|
||||
catch (IOException ioexception)
|
||||
{
|
||||
throw new Error("Couldn't bind resource pack icon", ioexception);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.locationTexturePackIcon == null)
|
||||
{
|
||||
this.locationTexturePackIcon = textureManagerIn.getDynamicTextureLocation("texturepackicon", new DynamicTexture(bufferedimage));
|
||||
}
|
||||
|
||||
textureManagerIn.bindTexture(this.locationTexturePackIcon);
|
||||
}
|
||||
|
||||
public void closeResourcePack()
|
||||
{
|
||||
if (this.reResourcePack instanceof Closeable)
|
||||
{
|
||||
IOUtils.closeQuietly((Closeable)this.reResourcePack);
|
||||
}
|
||||
}
|
||||
|
||||
public IResourcePack getResourcePack()
|
||||
{
|
||||
return this.reResourcePack;
|
||||
}
|
||||
|
||||
public String getResourcePackName()
|
||||
{
|
||||
return this.reResourcePack.getPackName();
|
||||
}
|
||||
|
||||
public String getTexturePackDescription()
|
||||
{
|
||||
return this.rePackMetadataSection == null ? TextFormatting.RED + "Invalid pack.mcmeta (or missing 'pack' section)" : this.rePackMetadataSection.getPackDescription().getFormattedText();
|
||||
}
|
||||
|
||||
public int getPackFormat()
|
||||
{
|
||||
return this.rePackMetadataSection == null ? 0 : this.rePackMetadataSection.getPackFormat();
|
||||
}
|
||||
|
||||
public boolean equals(Object p_equals_1_)
|
||||
{
|
||||
if (this == p_equals_1_)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return p_equals_1_ instanceof ResourcePackRepository.Entry ? this.toString().equals(p_equals_1_.toString()) : false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode()
|
||||
{
|
||||
return this.toString().hashCode();
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s:%s", this.reResourcePack.getPackName(), this.reResourcePack instanceof FolderResourcePack ? "folder" : "zip");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.client.resources.data.MetadataSerializer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class SimpleReloadableResourceManager implements IReloadableResourceManager
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final Joiner JOINER_RESOURCE_PACKS = Joiner.on(", ");
|
||||
private final Map<String, FallbackResourceManager> domainResourceManagers = Maps.<String, FallbackResourceManager>newHashMap();
|
||||
private final List<IResourceManagerReloadListener> reloadListeners = Lists.<IResourceManagerReloadListener>newArrayList();
|
||||
private final Set<String> setResourceDomains = Sets.<String>newLinkedHashSet();
|
||||
private final MetadataSerializer rmMetadataSerializer;
|
||||
|
||||
public SimpleReloadableResourceManager(MetadataSerializer rmMetadataSerializerIn)
|
||||
{
|
||||
this.rmMetadataSerializer = rmMetadataSerializerIn;
|
||||
}
|
||||
|
||||
public void reloadResourcePack(IResourcePack resourcePack)
|
||||
{
|
||||
for (String s : resourcePack.getResourceDomains())
|
||||
{
|
||||
this.setResourceDomains.add(s);
|
||||
FallbackResourceManager fallbackresourcemanager = this.domainResourceManagers.get(s);
|
||||
|
||||
if (fallbackresourcemanager == null)
|
||||
{
|
||||
fallbackresourcemanager = new FallbackResourceManager(this.rmMetadataSerializer);
|
||||
this.domainResourceManagers.put(s, fallbackresourcemanager);
|
||||
}
|
||||
|
||||
fallbackresourcemanager.addResourcePack(resourcePack);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getResourceDomains()
|
||||
{
|
||||
return this.setResourceDomains;
|
||||
}
|
||||
|
||||
public IResource getResource(ResourceLocation location) throws IOException
|
||||
{
|
||||
IResourceManager iresourcemanager = this.domainResourceManagers.get(location.getResourceDomain());
|
||||
|
||||
if (iresourcemanager != null)
|
||||
{
|
||||
return iresourcemanager.getResource(location);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException(location.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public List<IResource> getAllResources(ResourceLocation location) throws IOException
|
||||
{
|
||||
IResourceManager iresourcemanager = this.domainResourceManagers.get(location.getResourceDomain());
|
||||
|
||||
if (iresourcemanager != null)
|
||||
{
|
||||
return iresourcemanager.getAllResources(location);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException(location.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void clearResources()
|
||||
{
|
||||
this.domainResourceManagers.clear();
|
||||
this.setResourceDomains.clear();
|
||||
}
|
||||
|
||||
public void reloadResources(List<IResourcePack> resourcesPacksList)
|
||||
{
|
||||
net.minecraftforge.fml.common.ProgressManager.ProgressBar resReload = net.minecraftforge.fml.common.ProgressManager.push("Loading Resources", resourcesPacksList.size()+1, true);
|
||||
this.clearResources();
|
||||
LOGGER.info("Reloading ResourceManager: {}", (Object)JOINER_RESOURCE_PACKS.join(Iterables.transform(resourcesPacksList, new Function<IResourcePack, String>()
|
||||
{
|
||||
public String apply(@Nullable IResourcePack p_apply_1_)
|
||||
{
|
||||
return p_apply_1_ == null ? "<NULL>" : p_apply_1_.getPackName();
|
||||
}
|
||||
})));
|
||||
|
||||
for (IResourcePack iresourcepack : resourcesPacksList)
|
||||
{
|
||||
resReload.step(iresourcepack.getPackName());
|
||||
this.reloadResourcePack(iresourcepack);
|
||||
}
|
||||
|
||||
resReload.step("Reloading listeners");
|
||||
this.notifyReloadListeners();
|
||||
net.minecraftforge.fml.common.ProgressManager.pop(resReload);
|
||||
}
|
||||
|
||||
public void registerReloadListener(IResourceManagerReloadListener reloadListener)
|
||||
{
|
||||
net.minecraftforge.fml.common.ProgressManager.ProgressBar resReload = net.minecraftforge.fml.common.ProgressManager.push("Loading Resource", 1);
|
||||
resReload.step(reloadListener.getClass());
|
||||
this.reloadListeners.add(reloadListener);
|
||||
reloadListener.onResourceManagerReload(this);
|
||||
net.minecraftforge.fml.common.ProgressManager.pop(resReload);
|
||||
}
|
||||
|
||||
private void notifyReloadListeners()
|
||||
{
|
||||
net.minecraftforge.fml.common.ProgressManager.ProgressBar resReload = net.minecraftforge.fml.common.ProgressManager.push("Reloading", this.reloadListeners.size());
|
||||
for (IResourceManagerReloadListener iresourcemanagerreloadlistener : this.reloadListeners)
|
||||
{
|
||||
resReload.step(iresourcemanagerreloadlistener.getClass());
|
||||
if (!net.minecraftforge.client.ForgeHooksClient.shouldUseVanillaReloadableListener(iresourcemanagerreloadlistener)) continue; // Forge: Selective reloading for vanilla listeners
|
||||
iresourcemanagerreloadlistener.onResourceManagerReload(this);
|
||||
}
|
||||
net.minecraftforge.fml.common.ProgressManager.pop(resReload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.client.resources.data.IMetadataSection;
|
||||
import net.minecraft.client.resources.data.MetadataSerializer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class SimpleResource implements IResource
|
||||
{
|
||||
private final Map<String, IMetadataSection> mapMetadataSections = Maps.<String, IMetadataSection>newHashMap();
|
||||
private final String resourcePackName;
|
||||
private final ResourceLocation srResourceLocation;
|
||||
private final InputStream resourceInputStream;
|
||||
private final InputStream mcmetaInputStream;
|
||||
private final MetadataSerializer srMetadataSerializer;
|
||||
private boolean mcmetaJsonChecked;
|
||||
private JsonObject mcmetaJson;
|
||||
|
||||
public SimpleResource(String resourcePackNameIn, ResourceLocation srResourceLocationIn, InputStream resourceInputStreamIn, InputStream mcmetaInputStreamIn, MetadataSerializer srMetadataSerializerIn)
|
||||
{
|
||||
this.resourcePackName = resourcePackNameIn;
|
||||
this.srResourceLocation = srResourceLocationIn;
|
||||
this.resourceInputStream = resourceInputStreamIn;
|
||||
this.mcmetaInputStream = mcmetaInputStreamIn;
|
||||
this.srMetadataSerializer = srMetadataSerializerIn;
|
||||
}
|
||||
|
||||
public ResourceLocation getResourceLocation()
|
||||
{
|
||||
return this.srResourceLocation;
|
||||
}
|
||||
|
||||
public InputStream getInputStream()
|
||||
{
|
||||
return this.resourceInputStream;
|
||||
}
|
||||
|
||||
public boolean hasMetadata()
|
||||
{
|
||||
return this.mcmetaInputStream != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T extends IMetadataSection> T getMetadata(String sectionName)
|
||||
{
|
||||
if (!this.hasMetadata())
|
||||
{
|
||||
return (T)null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.mcmetaJson == null && !this.mcmetaJsonChecked)
|
||||
{
|
||||
this.mcmetaJsonChecked = true;
|
||||
BufferedReader bufferedreader = null;
|
||||
|
||||
try
|
||||
{
|
||||
bufferedreader = new BufferedReader(new InputStreamReader(this.mcmetaInputStream, StandardCharsets.UTF_8));
|
||||
this.mcmetaJson = (new JsonParser()).parse(bufferedreader).getAsJsonObject();
|
||||
}
|
||||
finally
|
||||
{
|
||||
IOUtils.closeQuietly((Reader)bufferedreader);
|
||||
}
|
||||
}
|
||||
|
||||
T t = (T)this.mapMetadataSections.get(sectionName);
|
||||
|
||||
if (t == null)
|
||||
{
|
||||
t = this.srMetadataSerializer.parseMetadataSection(sectionName, this.mcmetaJson);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
public String getResourcePackName()
|
||||
{
|
||||
return this.resourcePackName;
|
||||
}
|
||||
|
||||
public boolean equals(Object p_equals_1_)
|
||||
{
|
||||
if (this == p_equals_1_)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (!(p_equals_1_ instanceof SimpleResource))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
SimpleResource simpleresource = (SimpleResource)p_equals_1_;
|
||||
|
||||
if (this.srResourceLocation != null)
|
||||
{
|
||||
if (!this.srResourceLocation.equals(simpleresource.srResourceLocation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (simpleresource.srResourceLocation != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.resourcePackName != null)
|
||||
{
|
||||
if (!this.resourcePackName.equals(simpleresource.resourcePackName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (simpleresource.resourcePackName != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode()
|
||||
{
|
||||
int i = this.resourcePackName != null ? this.resourcePackName.hashCode() : 0;
|
||||
i = 31 * i + (this.srResourceLocation != null ? this.srResourceLocation.hashCode() : 0);
|
||||
return i;
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
this.resourceInputStream.close();
|
||||
|
||||
if (this.mcmetaInputStream != null)
|
||||
{
|
||||
this.mcmetaInputStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.minecraft.InsecureTextureException;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.IImageBuffer;
|
||||
import net.minecraft.client.renderer.ImageBufferDownload;
|
||||
import net.minecraft.client.renderer.ThreadDownloadImageData;
|
||||
import net.minecraft.client.renderer.texture.ITextureObject;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class SkinManager
|
||||
{
|
||||
private static final ExecutorService THREAD_POOL = new ThreadPoolExecutor(0, 2, 1L, TimeUnit.MINUTES, new LinkedBlockingQueue());
|
||||
private final TextureManager textureManager;
|
||||
private final File skinCacheDir;
|
||||
private final MinecraftSessionService sessionService;
|
||||
private final LoadingCache<GameProfile, Map<Type, MinecraftProfileTexture>> skinCacheLoader;
|
||||
|
||||
public SkinManager(TextureManager textureManagerInstance, File skinCacheDirectory, MinecraftSessionService sessionService)
|
||||
{
|
||||
this.textureManager = textureManagerInstance;
|
||||
this.skinCacheDir = skinCacheDirectory;
|
||||
this.sessionService = sessionService;
|
||||
this.skinCacheLoader = CacheBuilder.newBuilder().expireAfterAccess(15L, TimeUnit.SECONDS).<GameProfile, Map<Type, MinecraftProfileTexture>>build(new CacheLoader<GameProfile, Map<Type, MinecraftProfileTexture>>()
|
||||
{
|
||||
public Map<Type, MinecraftProfileTexture> load(GameProfile p_load_1_) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
return Minecraft.getMinecraft().getSessionService().getTextures(p_load_1_, false);
|
||||
}
|
||||
catch (Throwable var3)
|
||||
{
|
||||
return Maps.<Type, MinecraftProfileTexture>newHashMap();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in the Skull renderer to fetch a skin. May download the skin if it's not in the cache
|
||||
*/
|
||||
public ResourceLocation loadSkin(MinecraftProfileTexture profileTexture, Type textureType)
|
||||
{
|
||||
return this.loadSkin(profileTexture, textureType, (SkinManager.SkinAvailableCallback)null);
|
||||
}
|
||||
|
||||
/**
|
||||
* May download the skin if its not in the cache, can be passed a SkinManager#SkinAvailableCallback for handling
|
||||
*/
|
||||
public ResourceLocation loadSkin(final MinecraftProfileTexture profileTexture, final Type textureType, @Nullable final SkinManager.SkinAvailableCallback skinAvailableCallback)
|
||||
{
|
||||
final ResourceLocation resourcelocation = new ResourceLocation("skins/" + profileTexture.getHash());
|
||||
ITextureObject itextureobject = this.textureManager.getTexture(resourcelocation);
|
||||
|
||||
if (itextureobject != null)
|
||||
{
|
||||
if (skinAvailableCallback != null)
|
||||
{
|
||||
skinAvailableCallback.skinAvailable(textureType, resourcelocation, profileTexture);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
File file1 = new File(this.skinCacheDir, profileTexture.getHash().length() > 2 ? profileTexture.getHash().substring(0, 2) : "xx");
|
||||
File file2 = new File(file1, profileTexture.getHash());
|
||||
final IImageBuffer iimagebuffer = textureType == Type.SKIN ? new ImageBufferDownload() : null;
|
||||
ThreadDownloadImageData threaddownloadimagedata = new ThreadDownloadImageData(file2, profileTexture.getUrl(), DefaultPlayerSkin.getDefaultSkinLegacy(), new IImageBuffer()
|
||||
{
|
||||
public BufferedImage parseUserSkin(BufferedImage image)
|
||||
{
|
||||
if (iimagebuffer != null)
|
||||
{
|
||||
image = iimagebuffer.parseUserSkin(image);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
public void skinAvailable()
|
||||
{
|
||||
if (iimagebuffer != null)
|
||||
{
|
||||
iimagebuffer.skinAvailable();
|
||||
}
|
||||
|
||||
if (skinAvailableCallback != null)
|
||||
{
|
||||
skinAvailableCallback.skinAvailable(textureType, resourcelocation, profileTexture);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.textureManager.loadTexture(resourcelocation, threaddownloadimagedata);
|
||||
}
|
||||
|
||||
return resourcelocation;
|
||||
}
|
||||
|
||||
public void loadProfileTextures(final GameProfile profile, final SkinManager.SkinAvailableCallback skinAvailableCallback, final boolean requireSecure)
|
||||
{
|
||||
THREAD_POOL.submit(new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
final Map<Type, MinecraftProfileTexture> map = Maps.<Type, MinecraftProfileTexture>newHashMap();
|
||||
|
||||
try
|
||||
{
|
||||
map.putAll(SkinManager.this.sessionService.getTextures(profile, requireSecure));
|
||||
}
|
||||
catch (InsecureTextureException var3)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
if (map.isEmpty() && profile.getId().equals(Minecraft.getMinecraft().getSession().getProfile().getId()))
|
||||
{
|
||||
profile.getProperties().clear();
|
||||
profile.getProperties().putAll(Minecraft.getMinecraft().getProfileProperties());
|
||||
map.putAll(SkinManager.this.sessionService.getTextures(profile, false));
|
||||
}
|
||||
|
||||
Minecraft.getMinecraft().addScheduledTask(new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
if (map.containsKey(Type.SKIN))
|
||||
{
|
||||
SkinManager.this.loadSkin(map.get(Type.SKIN), Type.SKIN, skinAvailableCallback);
|
||||
}
|
||||
|
||||
if (map.containsKey(Type.CAPE))
|
||||
{
|
||||
SkinManager.this.loadSkin(map.get(Type.CAPE), Type.CAPE, skinAvailableCallback);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Map<Type, MinecraftProfileTexture> loadSkinFromCache(GameProfile profile)
|
||||
{
|
||||
return (Map)this.skinCacheLoader.getUnchecked(profile);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public interface SkinAvailableCallback
|
||||
{
|
||||
void skinAvailable(Type typeIn, ResourceLocation location, MinecraftProfileTexture profileTexture);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class AnimationFrame
|
||||
{
|
||||
private final int frameIndex;
|
||||
private final int frameTime;
|
||||
|
||||
public AnimationFrame(int frameIndexIn)
|
||||
{
|
||||
this(frameIndexIn, -1);
|
||||
}
|
||||
|
||||
public AnimationFrame(int frameIndexIn, int frameTimeIn)
|
||||
{
|
||||
this.frameIndex = frameIndexIn;
|
||||
this.frameTime = frameTimeIn;
|
||||
}
|
||||
|
||||
public boolean hasNoTime()
|
||||
{
|
||||
return this.frameTime == -1;
|
||||
}
|
||||
|
||||
public int getFrameTime()
|
||||
{
|
||||
return this.frameTime;
|
||||
}
|
||||
|
||||
public int getFrameIndex()
|
||||
{
|
||||
return this.frameIndex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class AnimationMetadataSection implements IMetadataSection
|
||||
{
|
||||
private final List<AnimationFrame> animationFrames;
|
||||
private final int frameWidth;
|
||||
private final int frameHeight;
|
||||
private final int frameTime;
|
||||
private final boolean interpolate;
|
||||
|
||||
public AnimationMetadataSection(List<AnimationFrame> animationFramesIn, int frameWidthIn, int frameHeightIn, int frameTimeIn, boolean interpolateIn)
|
||||
{
|
||||
this.animationFrames = animationFramesIn;
|
||||
this.frameWidth = frameWidthIn;
|
||||
this.frameHeight = frameHeightIn;
|
||||
this.frameTime = frameTimeIn;
|
||||
this.interpolate = interpolateIn;
|
||||
}
|
||||
|
||||
public int getFrameHeight()
|
||||
{
|
||||
return this.frameHeight;
|
||||
}
|
||||
|
||||
public int getFrameWidth()
|
||||
{
|
||||
return this.frameWidth;
|
||||
}
|
||||
|
||||
public int getFrameCount()
|
||||
{
|
||||
return this.animationFrames.size();
|
||||
}
|
||||
|
||||
public int getFrameTime()
|
||||
{
|
||||
return this.frameTime;
|
||||
}
|
||||
|
||||
public boolean isInterpolate()
|
||||
{
|
||||
return this.interpolate;
|
||||
}
|
||||
|
||||
private AnimationFrame getAnimationFrame(int frame)
|
||||
{
|
||||
return this.animationFrames.get(frame);
|
||||
}
|
||||
|
||||
public int getFrameTimeSingle(int frame)
|
||||
{
|
||||
AnimationFrame animationframe = this.getAnimationFrame(frame);
|
||||
return animationframe.hasNoTime() ? this.frameTime : animationframe.getFrameTime();
|
||||
}
|
||||
|
||||
public boolean frameHasTime(int frame)
|
||||
{
|
||||
return !((AnimationFrame)this.animationFrames.get(frame)).hasNoTime();
|
||||
}
|
||||
|
||||
public int getFrameIndex(int frame)
|
||||
{
|
||||
return ((AnimationFrame)this.animationFrames.get(frame)).getFrameIndex();
|
||||
}
|
||||
|
||||
public Set<Integer> getFrameIndexSet()
|
||||
{
|
||||
Set<Integer> set = Sets.<Integer>newHashSet();
|
||||
|
||||
for (AnimationFrame animationframe : this.animationFrames)
|
||||
{
|
||||
set.add(Integer.valueOf(animationframe.getFrameIndex()));
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class AnimationMetadataSectionSerializer extends BaseMetadataSectionSerializer<AnimationMetadataSection> implements JsonSerializer<AnimationMetadataSection>
|
||||
{
|
||||
public AnimationMetadataSection deserialize(JsonElement p_deserialize_1_, Type p_deserialize_2_, JsonDeserializationContext p_deserialize_3_) throws JsonParseException
|
||||
{
|
||||
List<AnimationFrame> list = Lists.<AnimationFrame>newArrayList();
|
||||
JsonObject jsonobject = JsonUtils.getJsonObject(p_deserialize_1_, "metadata section");
|
||||
int i = JsonUtils.getInt(jsonobject, "frametime", 1);
|
||||
|
||||
if (i != 1)
|
||||
{
|
||||
Validate.inclusiveBetween(1L, 2147483647L, (long)i, "Invalid default frame time");
|
||||
}
|
||||
|
||||
if (jsonobject.has("frames"))
|
||||
{
|
||||
try
|
||||
{
|
||||
JsonArray jsonarray = JsonUtils.getJsonArray(jsonobject, "frames");
|
||||
|
||||
for (int j = 0; j < jsonarray.size(); ++j)
|
||||
{
|
||||
JsonElement jsonelement = jsonarray.get(j);
|
||||
AnimationFrame animationframe = this.parseAnimationFrame(j, jsonelement);
|
||||
|
||||
if (animationframe != null)
|
||||
{
|
||||
list.add(animationframe);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ClassCastException classcastexception)
|
||||
{
|
||||
throw new JsonParseException("Invalid animation->frames: expected array, was " + jsonobject.get("frames"), classcastexception);
|
||||
}
|
||||
}
|
||||
|
||||
int k = JsonUtils.getInt(jsonobject, "width", -1);
|
||||
int l = JsonUtils.getInt(jsonobject, "height", -1);
|
||||
|
||||
if (k != -1)
|
||||
{
|
||||
Validate.inclusiveBetween(1L, 2147483647L, (long)k, "Invalid width");
|
||||
}
|
||||
|
||||
if (l != -1)
|
||||
{
|
||||
Validate.inclusiveBetween(1L, 2147483647L, (long)l, "Invalid height");
|
||||
}
|
||||
|
||||
boolean flag = JsonUtils.getBoolean(jsonobject, "interpolate", false);
|
||||
return new AnimationMetadataSection(list, k, l, i, flag);
|
||||
}
|
||||
|
||||
private AnimationFrame parseAnimationFrame(int frame, JsonElement element)
|
||||
{
|
||||
if (element.isJsonPrimitive())
|
||||
{
|
||||
return new AnimationFrame(JsonUtils.getInt(element, "frames[" + frame + "]"));
|
||||
}
|
||||
else if (element.isJsonObject())
|
||||
{
|
||||
JsonObject jsonobject = JsonUtils.getJsonObject(element, "frames[" + frame + "]");
|
||||
int i = JsonUtils.getInt(jsonobject, "time", -1);
|
||||
|
||||
if (jsonobject.has("time"))
|
||||
{
|
||||
Validate.inclusiveBetween(1L, 2147483647L, (long)i, "Invalid frame time");
|
||||
}
|
||||
|
||||
int j = JsonUtils.getInt(jsonobject, "index");
|
||||
Validate.inclusiveBetween(0L, 2147483647L, (long)j, "Invalid frame index");
|
||||
return new AnimationFrame(j, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public JsonElement serialize(AnimationMetadataSection p_serialize_1_, Type p_serialize_2_, JsonSerializationContext p_serialize_3_)
|
||||
{
|
||||
JsonObject jsonobject = new JsonObject();
|
||||
jsonobject.addProperty("frametime", Integer.valueOf(p_serialize_1_.getFrameTime()));
|
||||
|
||||
if (p_serialize_1_.getFrameWidth() != -1)
|
||||
{
|
||||
jsonobject.addProperty("width", Integer.valueOf(p_serialize_1_.getFrameWidth()));
|
||||
}
|
||||
|
||||
if (p_serialize_1_.getFrameHeight() != -1)
|
||||
{
|
||||
jsonobject.addProperty("height", Integer.valueOf(p_serialize_1_.getFrameHeight()));
|
||||
}
|
||||
|
||||
if (p_serialize_1_.getFrameCount() > 0)
|
||||
{
|
||||
JsonArray jsonarray = new JsonArray();
|
||||
|
||||
for (int i = 0; i < p_serialize_1_.getFrameCount(); ++i)
|
||||
{
|
||||
if (p_serialize_1_.frameHasTime(i))
|
||||
{
|
||||
JsonObject jsonobject1 = new JsonObject();
|
||||
jsonobject1.addProperty("index", Integer.valueOf(p_serialize_1_.getFrameIndex(i)));
|
||||
jsonobject1.addProperty("time", Integer.valueOf(p_serialize_1_.getFrameTimeSingle(i)));
|
||||
jsonarray.add(jsonobject1);
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonarray.add(new JsonPrimitive(p_serialize_1_.getFrameIndex(i)));
|
||||
}
|
||||
}
|
||||
|
||||
jsonobject.add("frames", jsonarray);
|
||||
}
|
||||
|
||||
return jsonobject;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of this section type as it appears in JSON.
|
||||
*/
|
||||
public String getSectionName()
|
||||
{
|
||||
return "animation";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public abstract class BaseMetadataSectionSerializer<T extends IMetadataSection> implements IMetadataSectionSerializer<T>
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class FontMetadataSection implements IMetadataSection
|
||||
{
|
||||
private final float[] charWidths;
|
||||
private final float[] charLefts;
|
||||
private final float[] charSpacings;
|
||||
|
||||
public FontMetadataSection(float[] charWidthsIn, float[] charLeftsIn, float[] charSpacingsIn)
|
||||
{
|
||||
this.charWidths = charWidthsIn;
|
||||
this.charLefts = charLeftsIn;
|
||||
this.charSpacings = charSpacingsIn;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import java.lang.reflect.Type;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class FontMetadataSectionSerializer extends BaseMetadataSectionSerializer<FontMetadataSection>
|
||||
{
|
||||
public FontMetadataSection deserialize(JsonElement p_deserialize_1_, Type p_deserialize_2_, JsonDeserializationContext p_deserialize_3_) throws JsonParseException
|
||||
{
|
||||
JsonObject jsonobject = p_deserialize_1_.getAsJsonObject();
|
||||
float[] afloat = new float[256];
|
||||
float[] afloat1 = new float[256];
|
||||
float[] afloat2 = new float[256];
|
||||
float f = 1.0F;
|
||||
float f1 = 0.0F;
|
||||
float f2 = 0.0F;
|
||||
|
||||
if (jsonobject.has("characters"))
|
||||
{
|
||||
if (!jsonobject.get("characters").isJsonObject())
|
||||
{
|
||||
throw new JsonParseException("Invalid font->characters: expected object, was " + jsonobject.get("characters"));
|
||||
}
|
||||
|
||||
JsonObject jsonobject1 = jsonobject.getAsJsonObject("characters");
|
||||
|
||||
if (jsonobject1.has("default"))
|
||||
{
|
||||
if (!jsonobject1.get("default").isJsonObject())
|
||||
{
|
||||
throw new JsonParseException("Invalid font->characters->default: expected object, was " + jsonobject1.get("default"));
|
||||
}
|
||||
|
||||
JsonObject jsonobject2 = jsonobject1.getAsJsonObject("default");
|
||||
f = JsonUtils.getFloat(jsonobject2, "width", f);
|
||||
Validate.inclusiveBetween(0.0D, 3.4028234663852886E38D, (double)f, "Invalid default width");
|
||||
f1 = JsonUtils.getFloat(jsonobject2, "spacing", f1);
|
||||
Validate.inclusiveBetween(0.0D, 3.4028234663852886E38D, (double)f1, "Invalid default spacing");
|
||||
f2 = JsonUtils.getFloat(jsonobject2, "left", f1);
|
||||
Validate.inclusiveBetween(0.0D, 3.4028234663852886E38D, (double)f2, "Invalid default left");
|
||||
}
|
||||
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
JsonElement jsonelement = jsonobject1.get(Integer.toString(i));
|
||||
float f3 = f;
|
||||
float f4 = f1;
|
||||
float f5 = f2;
|
||||
|
||||
if (jsonelement != null)
|
||||
{
|
||||
JsonObject jsonobject3 = JsonUtils.getJsonObject(jsonelement, "characters[" + i + "]");
|
||||
f3 = JsonUtils.getFloat(jsonobject3, "width", f);
|
||||
Validate.inclusiveBetween(0.0D, 3.4028234663852886E38D, (double)f3, "Invalid width");
|
||||
f4 = JsonUtils.getFloat(jsonobject3, "spacing", f1);
|
||||
Validate.inclusiveBetween(0.0D, 3.4028234663852886E38D, (double)f4, "Invalid spacing");
|
||||
f5 = JsonUtils.getFloat(jsonobject3, "left", f2);
|
||||
Validate.inclusiveBetween(0.0D, 3.4028234663852886E38D, (double)f5, "Invalid left");
|
||||
}
|
||||
|
||||
afloat[i] = f3;
|
||||
afloat1[i] = f4;
|
||||
afloat2[i] = f5;
|
||||
}
|
||||
}
|
||||
|
||||
return new FontMetadataSection(afloat, afloat2, afloat1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of this section type as it appears in JSON.
|
||||
*/
|
||||
public String getSectionName()
|
||||
{
|
||||
return "font";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public interface IMetadataSection
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public interface IMetadataSectionSerializer<T extends IMetadataSection> extends JsonDeserializer<T>
|
||||
{
|
||||
/**
|
||||
* The name of this section type as it appears in JSON.
|
||||
*/
|
||||
String getSectionName();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import java.util.Collection;
|
||||
import net.minecraft.client.resources.Language;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class LanguageMetadataSection implements IMetadataSection
|
||||
{
|
||||
private final Collection<Language> languages;
|
||||
|
||||
public LanguageMetadataSection(Collection<Language> languagesIn)
|
||||
{
|
||||
this.languages = languagesIn;
|
||||
}
|
||||
|
||||
public Collection<Language> getLanguages()
|
||||
{
|
||||
return this.languages;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import net.minecraft.client.resources.Language;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class LanguageMetadataSectionSerializer extends BaseMetadataSectionSerializer<LanguageMetadataSection>
|
||||
{
|
||||
public LanguageMetadataSection deserialize(JsonElement p_deserialize_1_, Type p_deserialize_2_, JsonDeserializationContext p_deserialize_3_) throws JsonParseException
|
||||
{
|
||||
JsonObject jsonobject = p_deserialize_1_.getAsJsonObject();
|
||||
Set<Language> set = Sets.<Language>newHashSet();
|
||||
|
||||
for (Entry<String, JsonElement> entry : jsonobject.entrySet())
|
||||
{
|
||||
String s = entry.getKey();
|
||||
|
||||
if (s.length() > 16)
|
||||
{
|
||||
throw new JsonParseException("Invalid language->'" + s + "': language code must not be more than " + 16 + " characters long");
|
||||
}
|
||||
|
||||
JsonObject jsonobject1 = JsonUtils.getJsonObject(entry.getValue(), "language");
|
||||
String s1 = JsonUtils.getString(jsonobject1, "region");
|
||||
String s2 = JsonUtils.getString(jsonobject1, "name");
|
||||
boolean flag = JsonUtils.getBoolean(jsonobject1, "bidirectional", false);
|
||||
|
||||
if (s1.isEmpty())
|
||||
{
|
||||
throw new JsonParseException("Invalid language->'" + s + "'->region: empty value");
|
||||
}
|
||||
|
||||
if (s2.isEmpty())
|
||||
{
|
||||
throw new JsonParseException("Invalid language->'" + s + "'->name: empty value");
|
||||
}
|
||||
|
||||
if (!set.add(new Language(s, s1, s2, flag)))
|
||||
{
|
||||
throw new JsonParseException("Duplicate language->'" + s + "' defined");
|
||||
}
|
||||
}
|
||||
|
||||
return new LanguageMetadataSection(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of this section type as it appears in JSON.
|
||||
*/
|
||||
public String getSectionName()
|
||||
{
|
||||
return "language";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.util.EnumTypeAdapterFactory;
|
||||
import net.minecraft.util.registry.IRegistry;
|
||||
import net.minecraft.util.registry.RegistrySimple;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.Style;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class MetadataSerializer
|
||||
{
|
||||
private final IRegistry < String, MetadataSerializer.Registration <? extends IMetadataSection >> metadataSectionSerializerRegistry = new RegistrySimple < String, MetadataSerializer.Registration <? extends IMetadataSection >> ();
|
||||
private final GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
/** Cached Gson instance. Set to null when more sections are registered, and then re-created from the builder. */
|
||||
private Gson gson;
|
||||
|
||||
public MetadataSerializer()
|
||||
{
|
||||
this.gsonBuilder.registerTypeHierarchyAdapter(ITextComponent.class, new ITextComponent.Serializer());
|
||||
this.gsonBuilder.registerTypeHierarchyAdapter(Style.class, new Style.Serializer());
|
||||
this.gsonBuilder.registerTypeAdapterFactory(new EnumTypeAdapterFactory());
|
||||
}
|
||||
|
||||
public <T extends IMetadataSection> void registerMetadataSectionType(IMetadataSectionSerializer<T> metadataSectionSerializer, Class<T> clazz)
|
||||
{
|
||||
this.metadataSectionSerializerRegistry.putObject(metadataSectionSerializer.getSectionName(), new MetadataSerializer.Registration(metadataSectionSerializer, clazz));
|
||||
this.gsonBuilder.registerTypeAdapter(clazz, metadataSectionSerializer);
|
||||
this.gson = null;
|
||||
}
|
||||
|
||||
public <T extends IMetadataSection> T parseMetadataSection(String sectionName, JsonObject json)
|
||||
{
|
||||
if (sectionName == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Metadata section name cannot be null");
|
||||
}
|
||||
else if (!json.has(sectionName))
|
||||
{
|
||||
return (T)null;
|
||||
}
|
||||
else if (!json.get(sectionName).isJsonObject())
|
||||
{
|
||||
throw new IllegalArgumentException("Invalid metadata for '" + sectionName + "' - expected object, found " + json.get(sectionName));
|
||||
}
|
||||
else
|
||||
{
|
||||
MetadataSerializer.Registration<?> registration = (MetadataSerializer.Registration)this.metadataSectionSerializerRegistry.getObject(sectionName);
|
||||
|
||||
if (registration == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Don't know how to handle metadata section '" + sectionName + "'");
|
||||
}
|
||||
else
|
||||
{
|
||||
return (T)(this.getGson().fromJson(json.getAsJsonObject(sectionName), registration.clazz));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Gson instance with type adapters registered for metadata sections.
|
||||
*/
|
||||
private Gson getGson()
|
||||
{
|
||||
if (this.gson == null)
|
||||
{
|
||||
this.gson = this.gsonBuilder.create();
|
||||
}
|
||||
|
||||
return this.gson;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
class Registration<T extends IMetadataSection>
|
||||
{
|
||||
/** The IMetadataSectionSerializer associated with the class registered */
|
||||
final IMetadataSectionSerializer<T> section;
|
||||
/** The class registered */
|
||||
final Class<T> clazz;
|
||||
|
||||
private Registration(IMetadataSectionSerializer<T> metadataSectionSerializer, Class<T> clazzToRegister)
|
||||
{
|
||||
this.section = metadataSectionSerializer;
|
||||
this.clazz = clazzToRegister;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class PackMetadataSection implements IMetadataSection
|
||||
{
|
||||
private final ITextComponent packDescription;
|
||||
private final int packFormat;
|
||||
|
||||
public PackMetadataSection(ITextComponent packDescriptionIn, int packFormatIn)
|
||||
{
|
||||
this.packDescription = packDescriptionIn;
|
||||
this.packFormat = packFormatIn;
|
||||
}
|
||||
|
||||
public ITextComponent getPackDescription()
|
||||
{
|
||||
return this.packDescription;
|
||||
}
|
||||
|
||||
public int getPackFormat()
|
||||
{
|
||||
return this.packFormat;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import java.lang.reflect.Type;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class PackMetadataSectionSerializer extends BaseMetadataSectionSerializer<PackMetadataSection> implements JsonSerializer<PackMetadataSection>
|
||||
{
|
||||
public PackMetadataSection deserialize(JsonElement p_deserialize_1_, Type p_deserialize_2_, JsonDeserializationContext p_deserialize_3_) throws JsonParseException
|
||||
{
|
||||
JsonObject jsonobject = p_deserialize_1_.getAsJsonObject();
|
||||
ITextComponent itextcomponent = (ITextComponent)p_deserialize_3_.deserialize(jsonobject.get("description"), ITextComponent.class);
|
||||
|
||||
if (itextcomponent == null)
|
||||
{
|
||||
throw new JsonParseException("Invalid/missing description!");
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = JsonUtils.getInt(jsonobject, "pack_format");
|
||||
return new PackMetadataSection(itextcomponent, i);
|
||||
}
|
||||
}
|
||||
|
||||
public JsonElement serialize(PackMetadataSection p_serialize_1_, Type p_serialize_2_, JsonSerializationContext p_serialize_3_)
|
||||
{
|
||||
JsonObject jsonobject = new JsonObject();
|
||||
jsonobject.addProperty("pack_format", Integer.valueOf(p_serialize_1_.getPackFormat()));
|
||||
jsonobject.add("description", p_serialize_3_.serialize(p_serialize_1_.getPackDescription()));
|
||||
return jsonobject;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of this section type as it appears in JSON.
|
||||
*/
|
||||
public String getSectionName()
|
||||
{
|
||||
return "pack";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class TextureMetadataSection implements IMetadataSection
|
||||
{
|
||||
private final boolean textureBlur;
|
||||
private final boolean textureClamp;
|
||||
|
||||
public TextureMetadataSection(boolean textureBlurIn, boolean textureClampIn)
|
||||
{
|
||||
this.textureBlur = textureBlurIn;
|
||||
this.textureClamp = textureClampIn;
|
||||
}
|
||||
|
||||
public boolean getTextureBlur()
|
||||
{
|
||||
return this.textureBlur;
|
||||
}
|
||||
|
||||
public boolean getTextureClamp()
|
||||
{
|
||||
return this.textureClamp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import java.lang.reflect.Type;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class TextureMetadataSectionSerializer extends BaseMetadataSectionSerializer<TextureMetadataSection>
|
||||
{
|
||||
public TextureMetadataSection deserialize(JsonElement p_deserialize_1_, Type p_deserialize_2_, JsonDeserializationContext p_deserialize_3_) throws JsonParseException
|
||||
{
|
||||
JsonObject jsonobject = p_deserialize_1_.getAsJsonObject();
|
||||
boolean flag = JsonUtils.getBoolean(jsonobject, "blur", false);
|
||||
boolean flag1 = JsonUtils.getBoolean(jsonobject, "clamp", false);
|
||||
return new TextureMetadataSection(flag, flag1);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of this section type as it appears in JSON.
|
||||
*/
|
||||
public String getSectionName()
|
||||
{
|
||||
return "texture";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Auto generated package-info by MCP
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package net.minecraft.client.resources.data;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -0,0 +1,7 @@
|
||||
// Auto generated package-info by MCP
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package net.minecraft.client.resources;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
Reference in New Issue
Block a user