mirror of https://github.com/GeyserMC/Geyser.git
Compare commits
3 Commits
19d95a3193
...
46f19dd6f7
Author | SHA1 | Date |
---|---|---|
rtm516 | 46f19dd6f7 | |
rtm516 | 92e4439075 | |
rtm516 | d2a9ee53e8 |
|
@ -1,5 +1,6 @@
|
|||
<component name="CopyrightManager">
|
||||
<copyright>
|
||||
<option name="allowReplaceRegexp" value="Copyright" />
|
||||
<option name="notice" value="Copyright (c) &#36;originalComment.match("Copyright \(c\) (\d+)", 1, "-")&#36;today.year GeyserMC. http://geysermc.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @author GeyserMC @link https://github.com/GeyserMC/Geyser" />
|
||||
<option name="myName" value="Geyser" />
|
||||
</copyright>
|
||||
|
|
|
@ -46,7 +46,7 @@ public abstract class SessionSkinApplyEvent extends ConnectionEvent {
|
|||
private final UUID uuid;
|
||||
private final boolean slim;
|
||||
private final boolean bedrock;
|
||||
private final SkinData skinData;
|
||||
private final SkinData originalSkinData;
|
||||
|
||||
public SessionSkinApplyEvent(@NonNull GeyserConnection connection, String username, UUID uuid, boolean slim, boolean bedrock, SkinData skinData) {
|
||||
super(connection);
|
||||
|
@ -54,7 +54,7 @@ public abstract class SessionSkinApplyEvent extends ConnectionEvent {
|
|||
this.uuid = uuid;
|
||||
this.slim = slim;
|
||||
this.bedrock = bedrock;
|
||||
this.skinData = skinData;
|
||||
this.originalSkinData = skinData;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,14 +93,21 @@ public abstract class SessionSkinApplyEvent extends ConnectionEvent {
|
|||
return bedrock;
|
||||
}
|
||||
|
||||
/**
|
||||
* The original skin data of the player.
|
||||
*
|
||||
* @return the original skin data of the player
|
||||
*/
|
||||
public SkinData originalSkin() {
|
||||
return originalSkinData;
|
||||
}
|
||||
|
||||
/**
|
||||
* The skin data of the player.
|
||||
*
|
||||
* @return the skin data of the player
|
||||
*/
|
||||
public SkinData skinData() {
|
||||
return skinData;
|
||||
}
|
||||
public abstract SkinData skinData();
|
||||
|
||||
/**
|
||||
* Change the skin of the player.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -116,7 +116,7 @@ public class SkullResourcePackManager {
|
|||
return;
|
||||
}
|
||||
|
||||
BufferedImage image = SkinProvider.requestImage(skinUrl, null);
|
||||
BufferedImage image = SkinProvider.requestImage(skinUrl, false);
|
||||
// Resize skins to 48x16 to save on space and memory
|
||||
BufferedImage skullTexture = new BufferedImage(48, 16, image.getType());
|
||||
// Reorder skin parts to fit into the space
|
||||
|
|
|
@ -94,6 +94,7 @@ public class FakeHeadProvider {
|
|||
|
||||
// Avoiding memory leak
|
||||
fakeHeadEntry.setEntity(null);
|
||||
fakeHeadEntry.setSession(null);
|
||||
|
||||
return new SkinData(mergedSkin, cape, geometry);
|
||||
}
|
||||
|
|
|
@ -261,25 +261,31 @@ public class SkinProvider {
|
|||
|
||||
// Call event to allow extensions to modify the skin, cape and geo
|
||||
boolean isBedrock = GeyserImpl.getInstance().connectionByUuid(entity.getUuid()) != null;
|
||||
final SkinData[] skinData = {new SkinData(skin, cape, geometry)};
|
||||
GeyserImpl.getInstance().eventBus().fire(new SessionSkinApplyEvent(session, entity.getUsername(), entity.getUuid(), data.isAlex(), isBedrock, skinData[0]) {
|
||||
SkinData skinData = new SkinData(skin, cape, geometry);
|
||||
final EventSkinData eventSkinData = new EventSkinData(skinData);
|
||||
GeyserImpl.getInstance().eventBus().fire(new SessionSkinApplyEvent(session, entity.getUsername(), entity.getUuid(), data.isAlex(), isBedrock, skinData) {
|
||||
@Override
|
||||
public SkinData skinData() {
|
||||
return eventSkinData.skinData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skin(@NonNull Skin newSkin) {
|
||||
skinData[0] = new SkinData(newSkin, skinData[0].cape(), skinData[0].geometry());
|
||||
eventSkinData.skinData(new SkinData(newSkin, skinData.cape(), skinData.geometry()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cape(@NonNull Cape newCape) {
|
||||
skinData[0] = new SkinData(skinData[0].skin(), newCape, skinData[0].geometry());
|
||||
eventSkinData.skinData(new SkinData(skinData.skin(), newCape, skinData.geometry()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void geometry(@NonNull SkinGeometry newGeometry) {
|
||||
skinData[0] = new SkinData(skinData[0].skin(), skinData[0].cape(), newGeometry);
|
||||
eventSkinData.skinData(new SkinData(skinData.skin(), skinData.cape(), newGeometry));
|
||||
}
|
||||
});
|
||||
|
||||
return skinData[0];
|
||||
return eventSkinData.skinData();
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.skin.fail", entity.getUuid()), e);
|
||||
}
|
||||
|
@ -292,10 +298,9 @@ public class SkinProvider {
|
|||
return CompletableFuture.supplyAsync(() -> {
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
CapeProvider provider = capeUrl != null ? CapeProvider.MINECRAFT : null;
|
||||
SkinAndCape skinAndCape = new SkinAndCape(
|
||||
getOrDefault(requestSkin(playerId, skinUrl, false), EMPTY_SKIN, 5),
|
||||
getOrDefault(requestCape(capeUrl, provider, false), EMPTY_CAPE, 5)
|
||||
getOrDefault(requestCape(capeUrl, false), EMPTY_CAPE, 5)
|
||||
);
|
||||
|
||||
GeyserImpl.getInstance().getLogger().debug("Took " + (System.currentTimeMillis() - time) + "ms for " + playerId);
|
||||
|
@ -332,7 +337,7 @@ public class SkinProvider {
|
|||
return future;
|
||||
}
|
||||
|
||||
private static CompletableFuture<Cape> requestCape(String capeUrl, CapeProvider provider, boolean newThread) {
|
||||
private static CompletableFuture<Cape> requestCape(String capeUrl, boolean newThread) {
|
||||
if (capeUrl == null || capeUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_CAPE);
|
||||
CompletableFuture<Cape> requestedCape = requestedCapes.get(capeUrl);
|
||||
if (requestedCape != null) {
|
||||
|
@ -346,14 +351,14 @@ public class SkinProvider {
|
|||
|
||||
CompletableFuture<Cape> future;
|
||||
if (newThread) {
|
||||
future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl, provider), getExecutorService())
|
||||
future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl), getExecutorService())
|
||||
.whenCompleteAsync((cape, throwable) -> {
|
||||
CACHED_JAVA_CAPES.put(capeUrl, cape);
|
||||
requestedCapes.remove(capeUrl);
|
||||
});
|
||||
requestedCapes.put(capeUrl, future);
|
||||
} else {
|
||||
Cape cape = supplyCape(capeUrl, provider); // blocking
|
||||
Cape cape = supplyCape(capeUrl); // blocking
|
||||
future = CompletableFuture.completedFuture(cape);
|
||||
CACHED_JAVA_CAPES.put(capeUrl, cape);
|
||||
}
|
||||
|
@ -377,17 +382,17 @@ public class SkinProvider {
|
|||
|
||||
private static Skin supplySkin(UUID uuid, String textureUrl) {
|
||||
try {
|
||||
byte[] skin = requestImageData(textureUrl, null);
|
||||
byte[] skin = requestImageData(textureUrl, false);
|
||||
return new Skin(textureUrl, skin);
|
||||
} catch (Exception ignored) {} // just ignore I guess
|
||||
|
||||
return new Skin("empty", EMPTY_SKIN.skinData(), true);
|
||||
}
|
||||
|
||||
private static Cape supplyCape(String capeUrl, CapeProvider provider) {
|
||||
private static Cape supplyCape(String capeUrl) {
|
||||
byte[] cape = EMPTY_CAPE.capeData();
|
||||
try {
|
||||
cape = requestImageData(capeUrl, provider);
|
||||
cape = requestImageData(capeUrl, true);
|
||||
} catch (Exception ignored) {
|
||||
} // just ignore I guess
|
||||
|
||||
|
@ -402,7 +407,7 @@ public class SkinProvider {
|
|||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
public static BufferedImage requestImage(String imageUrl, CapeProvider provider) throws IOException {
|
||||
public static BufferedImage requestImage(String imageUrl, boolean isCape) throws IOException {
|
||||
BufferedImage image = null;
|
||||
|
||||
// First see if we have a cached file. We also update the modification stamp so we know when the file was last used
|
||||
|
@ -417,7 +422,7 @@ public class SkinProvider {
|
|||
|
||||
// If no image we download it
|
||||
if (image == null) {
|
||||
image = downloadImage(imageUrl, provider);
|
||||
image = downloadImage(imageUrl);
|
||||
GeyserImpl.getInstance().getLogger().debug("Downloaded " + imageUrl);
|
||||
|
||||
// Write to cache if we are allowed
|
||||
|
@ -433,7 +438,7 @@ public class SkinProvider {
|
|||
}
|
||||
|
||||
// if the requested image is a cape
|
||||
if (provider != null) {
|
||||
if (isCape) {
|
||||
if (image.getWidth() > 64 || image.getHeight() > 32) {
|
||||
// Prevent weirdly-scaled capes from being cut off
|
||||
BufferedImage newImage = new BufferedImage(128, 64, BufferedImage.TYPE_INT_ARGB);
|
||||
|
@ -465,8 +470,8 @@ public class SkinProvider {
|
|||
return image;
|
||||
}
|
||||
|
||||
private static byte[] requestImageData(String imageUrl, CapeProvider provider) throws Exception {
|
||||
BufferedImage image = requestImage(imageUrl, provider);
|
||||
private static byte[] requestImageData(String imageUrl, boolean isCape) throws Exception {
|
||||
BufferedImage image = requestImage(imageUrl, isCape);
|
||||
byte[] data = bufferedImageToImageData(image);
|
||||
image.flush();
|
||||
return data;
|
||||
|
@ -529,7 +534,7 @@ public class SkinProvider {
|
|||
});
|
||||
}
|
||||
|
||||
private static BufferedImage downloadImage(String imageUrl, CapeProvider provider) throws IOException {
|
||||
private static BufferedImage downloadImage(String imageUrl) throws IOException {
|
||||
HttpURLConnection con = (HttpURLConnection) new URL(imageUrl).openConnection();
|
||||
con.setRequestProperty("User-Agent", WebUtils.getUserAgent());
|
||||
con.setConnectTimeout(10000);
|
||||
|
@ -538,7 +543,7 @@ public class SkinProvider {
|
|||
BufferedImage image = ImageIO.read(con.getInputStream());
|
||||
|
||||
if (image == null) {
|
||||
throw new IllegalArgumentException("Failed to read image from: %s (cape provider=%s)".formatted(imageUrl, provider));
|
||||
throw new IllegalArgumentException("Failed to read image from: %s".formatted(imageUrl));
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
@ -616,39 +621,19 @@ public class SkinProvider {
|
|||
public record SkinAndCape(Skin skin, Cape cape) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Sorted by 'priority'
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
public enum CapeProvider {
|
||||
MINECRAFT;
|
||||
public static class EventSkinData {
|
||||
private SkinData skinData;
|
||||
|
||||
public static final CapeProvider[] VALUES = Arrays.copyOfRange(values(), 1, 5);
|
||||
private String url;
|
||||
private CapeUrlType type;
|
||||
|
||||
public String getUrlFor(String type) {
|
||||
return String.format(url, type);
|
||||
public EventSkinData(SkinData skinData) {
|
||||
this.skinData = skinData;
|
||||
}
|
||||
|
||||
public String getUrlFor(UUID uuid, String username) {
|
||||
return getUrlFor(toRequestedType(type, uuid, username));
|
||||
public SkinData skinData() {
|
||||
return skinData;
|
||||
}
|
||||
|
||||
public static String toRequestedType(CapeUrlType type, UUID uuid, String username) {
|
||||
return switch (type) {
|
||||
case UUID -> uuid.toString().replace("-", "");
|
||||
case UUID_DASHED -> uuid.toString();
|
||||
default -> username;
|
||||
};
|
||||
public void skinData(SkinData skinData) {
|
||||
this.skinData = skinData;
|
||||
}
|
||||
}
|
||||
|
||||
public enum CapeUrlType {
|
||||
USERNAME,
|
||||
UUID,
|
||||
UUID_DASHED
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,5 +7,5 @@ org.gradle.vfs.watch=false
|
|||
|
||||
group=org.geysermc
|
||||
id=geyser
|
||||
version=2.2.3-SNAPSHOT
|
||||
version=2.2.4-SNAPSHOT
|
||||
description=Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers.
|
||||
|
|
Loading…
Reference in New Issue