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">
|
<component name="CopyrightManager">
|
||||||
<copyright>
|
<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="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" />
|
<option name="myName" value="Geyser" />
|
||||||
</copyright>
|
</copyright>
|
||||||
|
|
|
@ -46,7 +46,7 @@ public abstract class SessionSkinApplyEvent extends ConnectionEvent {
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
private final boolean slim;
|
private final boolean slim;
|
||||||
private final boolean bedrock;
|
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) {
|
public SessionSkinApplyEvent(@NonNull GeyserConnection connection, String username, UUID uuid, boolean slim, boolean bedrock, SkinData skinData) {
|
||||||
super(connection);
|
super(connection);
|
||||||
|
@ -54,7 +54,7 @@ public abstract class SessionSkinApplyEvent extends ConnectionEvent {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.slim = slim;
|
this.slim = slim;
|
||||||
this.bedrock = bedrock;
|
this.bedrock = bedrock;
|
||||||
this.skinData = skinData;
|
this.originalSkinData = skinData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,14 +93,21 @@ public abstract class SessionSkinApplyEvent extends ConnectionEvent {
|
||||||
return bedrock;
|
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.
|
* The skin data of the player.
|
||||||
*
|
*
|
||||||
* @return the skin data of the player
|
* @return the skin data of the player
|
||||||
*/
|
*/
|
||||||
public SkinData skinData() {
|
public abstract SkinData skinData();
|
||||||
return skinData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the skin of the player.
|
* 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
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -116,7 +116,7 @@ public class SkullResourcePackManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferedImage image = SkinProvider.requestImage(skinUrl, null);
|
BufferedImage image = SkinProvider.requestImage(skinUrl, false);
|
||||||
// Resize skins to 48x16 to save on space and memory
|
// Resize skins to 48x16 to save on space and memory
|
||||||
BufferedImage skullTexture = new BufferedImage(48, 16, image.getType());
|
BufferedImage skullTexture = new BufferedImage(48, 16, image.getType());
|
||||||
// Reorder skin parts to fit into the space
|
// Reorder skin parts to fit into the space
|
||||||
|
|
|
@ -94,6 +94,7 @@ public class FakeHeadProvider {
|
||||||
|
|
||||||
// Avoiding memory leak
|
// Avoiding memory leak
|
||||||
fakeHeadEntry.setEntity(null);
|
fakeHeadEntry.setEntity(null);
|
||||||
|
fakeHeadEntry.setSession(null);
|
||||||
|
|
||||||
return new SkinData(mergedSkin, cape, geometry);
|
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
|
// Call event to allow extensions to modify the skin, cape and geo
|
||||||
boolean isBedrock = GeyserImpl.getInstance().connectionByUuid(entity.getUuid()) != null;
|
boolean isBedrock = GeyserImpl.getInstance().connectionByUuid(entity.getUuid()) != null;
|
||||||
final SkinData[] skinData = {new SkinData(skin, cape, geometry)};
|
SkinData skinData = new SkinData(skin, cape, geometry);
|
||||||
GeyserImpl.getInstance().eventBus().fire(new SessionSkinApplyEvent(session, entity.getUsername(), entity.getUuid(), data.isAlex(), isBedrock, skinData[0]) {
|
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
|
@Override
|
||||||
public void skin(@NonNull Skin newSkin) {
|
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
|
@Override
|
||||||
public void cape(@NonNull Cape newCape) {
|
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
|
@Override
|
||||||
public void geometry(@NonNull SkinGeometry newGeometry) {
|
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) {
|
} catch (Exception e) {
|
||||||
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.skin.fail", entity.getUuid()), e);
|
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.skin.fail", entity.getUuid()), e);
|
||||||
}
|
}
|
||||||
|
@ -292,10 +298,9 @@ public class SkinProvider {
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
|
|
||||||
CapeProvider provider = capeUrl != null ? CapeProvider.MINECRAFT : null;
|
|
||||||
SkinAndCape skinAndCape = new SkinAndCape(
|
SkinAndCape skinAndCape = new SkinAndCape(
|
||||||
getOrDefault(requestSkin(playerId, skinUrl, false), EMPTY_SKIN, 5),
|
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);
|
GeyserImpl.getInstance().getLogger().debug("Took " + (System.currentTimeMillis() - time) + "ms for " + playerId);
|
||||||
|
@ -332,7 +337,7 @@ public class SkinProvider {
|
||||||
return future;
|
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);
|
if (capeUrl == null || capeUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_CAPE);
|
||||||
CompletableFuture<Cape> requestedCape = requestedCapes.get(capeUrl);
|
CompletableFuture<Cape> requestedCape = requestedCapes.get(capeUrl);
|
||||||
if (requestedCape != null) {
|
if (requestedCape != null) {
|
||||||
|
@ -346,14 +351,14 @@ public class SkinProvider {
|
||||||
|
|
||||||
CompletableFuture<Cape> future;
|
CompletableFuture<Cape> future;
|
||||||
if (newThread) {
|
if (newThread) {
|
||||||
future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl, provider), getExecutorService())
|
future = CompletableFuture.supplyAsync(() -> supplyCape(capeUrl), getExecutorService())
|
||||||
.whenCompleteAsync((cape, throwable) -> {
|
.whenCompleteAsync((cape, throwable) -> {
|
||||||
CACHED_JAVA_CAPES.put(capeUrl, cape);
|
CACHED_JAVA_CAPES.put(capeUrl, cape);
|
||||||
requestedCapes.remove(capeUrl);
|
requestedCapes.remove(capeUrl);
|
||||||
});
|
});
|
||||||
requestedCapes.put(capeUrl, future);
|
requestedCapes.put(capeUrl, future);
|
||||||
} else {
|
} else {
|
||||||
Cape cape = supplyCape(capeUrl, provider); // blocking
|
Cape cape = supplyCape(capeUrl); // blocking
|
||||||
future = CompletableFuture.completedFuture(cape);
|
future = CompletableFuture.completedFuture(cape);
|
||||||
CACHED_JAVA_CAPES.put(capeUrl, cape);
|
CACHED_JAVA_CAPES.put(capeUrl, cape);
|
||||||
}
|
}
|
||||||
|
@ -377,17 +382,17 @@ public class SkinProvider {
|
||||||
|
|
||||||
private static Skin supplySkin(UUID uuid, String textureUrl) {
|
private static Skin supplySkin(UUID uuid, String textureUrl) {
|
||||||
try {
|
try {
|
||||||
byte[] skin = requestImageData(textureUrl, null);
|
byte[] skin = requestImageData(textureUrl, false);
|
||||||
return new Skin(textureUrl, skin);
|
return new Skin(textureUrl, skin);
|
||||||
} catch (Exception ignored) {} // just ignore I guess
|
} catch (Exception ignored) {} // just ignore I guess
|
||||||
|
|
||||||
return new Skin("empty", EMPTY_SKIN.skinData(), true);
|
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();
|
byte[] cape = EMPTY_CAPE.capeData();
|
||||||
try {
|
try {
|
||||||
cape = requestImageData(capeUrl, provider);
|
cape = requestImageData(capeUrl, true);
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
} // just ignore I guess
|
} // just ignore I guess
|
||||||
|
|
||||||
|
@ -402,7 +407,7 @@ public class SkinProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
@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;
|
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
|
// 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 no image we download it
|
||||||
if (image == null) {
|
if (image == null) {
|
||||||
image = downloadImage(imageUrl, provider);
|
image = downloadImage(imageUrl);
|
||||||
GeyserImpl.getInstance().getLogger().debug("Downloaded " + imageUrl);
|
GeyserImpl.getInstance().getLogger().debug("Downloaded " + imageUrl);
|
||||||
|
|
||||||
// Write to cache if we are allowed
|
// Write to cache if we are allowed
|
||||||
|
@ -433,7 +438,7 @@ public class SkinProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the requested image is a cape
|
// if the requested image is a cape
|
||||||
if (provider != null) {
|
if (isCape) {
|
||||||
if (image.getWidth() > 64 || image.getHeight() > 32) {
|
if (image.getWidth() > 64 || image.getHeight() > 32) {
|
||||||
// Prevent weirdly-scaled capes from being cut off
|
// Prevent weirdly-scaled capes from being cut off
|
||||||
BufferedImage newImage = new BufferedImage(128, 64, BufferedImage.TYPE_INT_ARGB);
|
BufferedImage newImage = new BufferedImage(128, 64, BufferedImage.TYPE_INT_ARGB);
|
||||||
|
@ -465,8 +470,8 @@ public class SkinProvider {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] requestImageData(String imageUrl, CapeProvider provider) throws Exception {
|
private static byte[] requestImageData(String imageUrl, boolean isCape) throws Exception {
|
||||||
BufferedImage image = requestImage(imageUrl, provider);
|
BufferedImage image = requestImage(imageUrl, isCape);
|
||||||
byte[] data = bufferedImageToImageData(image);
|
byte[] data = bufferedImageToImageData(image);
|
||||||
image.flush();
|
image.flush();
|
||||||
return data;
|
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();
|
HttpURLConnection con = (HttpURLConnection) new URL(imageUrl).openConnection();
|
||||||
con.setRequestProperty("User-Agent", WebUtils.getUserAgent());
|
con.setRequestProperty("User-Agent", WebUtils.getUserAgent());
|
||||||
con.setConnectTimeout(10000);
|
con.setConnectTimeout(10000);
|
||||||
|
@ -538,7 +543,7 @@ public class SkinProvider {
|
||||||
BufferedImage image = ImageIO.read(con.getInputStream());
|
BufferedImage image = ImageIO.read(con.getInputStream());
|
||||||
|
|
||||||
if (image == null) {
|
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;
|
return image;
|
||||||
}
|
}
|
||||||
|
@ -616,39 +621,19 @@ public class SkinProvider {
|
||||||
public record SkinAndCape(Skin skin, Cape cape) {
|
public record SkinAndCape(Skin skin, Cape cape) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
public static class EventSkinData {
|
||||||
* Sorted by 'priority'
|
private SkinData skinData;
|
||||||
*/
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
@Getter
|
|
||||||
public enum CapeProvider {
|
|
||||||
MINECRAFT;
|
|
||||||
|
|
||||||
public static final CapeProvider[] VALUES = Arrays.copyOfRange(values(), 1, 5);
|
public EventSkinData(SkinData skinData) {
|
||||||
private String url;
|
this.skinData = skinData;
|
||||||
private CapeUrlType type;
|
|
||||||
|
|
||||||
public String getUrlFor(String type) {
|
|
||||||
return String.format(url, type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUrlFor(UUID uuid, String username) {
|
public SkinData skinData() {
|
||||||
return getUrlFor(toRequestedType(type, uuid, username));
|
return skinData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toRequestedType(CapeUrlType type, UUID uuid, String username) {
|
public void skinData(SkinData skinData) {
|
||||||
return switch (type) {
|
this.skinData = skinData;
|
||||||
case UUID -> uuid.toString().replace("-", "");
|
|
||||||
case UUID_DASHED -> uuid.toString();
|
|
||||||
default -> username;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum CapeUrlType {
|
|
||||||
USERNAME,
|
|
||||||
UUID,
|
|
||||||
UUID_DASHED
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,5 @@ org.gradle.vfs.watch=false
|
||||||
|
|
||||||
group=org.geysermc
|
group=org.geysermc
|
||||||
id=geyser
|
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.
|
description=Allows for players from Minecraft: Bedrock Edition to join Minecraft: Java Edition servers.
|
||||||
|
|
Loading…
Reference in New Issue