chore: cleanup. Closes #989.

This commit is contained in:
huanghongxun 2021-09-25 02:11:31 +08:00
parent cd030c1de0
commit c60df2c26b
15 changed files with 914 additions and 921 deletions

View File

@ -1,67 +1,68 @@
package moe.mickey.minecraft.skin.fx; package moe.mickey.minecraft.skin.fx;
import javafx.event.Event;
import javafx.event.EventHandler;
import java.util.Arrays; import java.util.Arrays;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import javafx.event.Event;
import javafx.event.EventHandler;
public final class FunctionHelper { public final class FunctionHelper {
private FunctionHelper() {
}
@SafeVarargs private FunctionHelper() {
public static <T> void always(Consumer<T> consumer, T... ts) { }
Arrays.asList(ts).forEach(consumer);
}
@SafeVarargs @SafeVarargs
public static <A, B> void alwaysA(BiConsumer<A, B> consumer, A a, B... bs) { public static <T> void always(Consumer<T> consumer, T... ts) {
Arrays.asList(bs).forEach(b -> consumer.accept(a, b)); Arrays.asList(ts).forEach(consumer);
} }
@SafeVarargs @SafeVarargs
public static <A, B> void alwaysB(BiConsumer<A, B> consumer, B b, A... as) { public static <A, B> void alwaysA(BiConsumer<A, B> consumer, A a, B... bs) {
Arrays.asList(as).forEach(a -> consumer.accept(a, b)); Arrays.asList(bs).forEach(b -> consumer.accept(a, b));
} }
public static <A, B> BiConsumer<B, A> exchange(BiConsumer<A, B> consumer) { @SafeVarargs
return (b, a) -> consumer.accept(a, b); public static <A, B> void alwaysB(BiConsumer<A, B> consumer, B b, A... as) {
} Arrays.asList(as).forEach(a -> consumer.accept(a, b));
}
@SafeVarargs public static <A, B> BiConsumer<B, A> exchange(BiConsumer<A, B> consumer) {
public static <T> Consumer<T> link(Consumer<T>... consumers) { return (b, a) -> consumer.accept(a, b);
return t -> { }
for (Consumer<T> consumer : consumers)
consumer.accept(t);
};
}
@SafeVarargs @SafeVarargs
public static <T extends Event> EventHandler<T> link(EventHandler<T>... handlers) { public static <T> Consumer<T> link(Consumer<T>... consumers) {
return t -> { return t -> {
for (EventHandler<T> handler : handlers) for (Consumer<T> consumer : consumers)
handler.handle(t); consumer.accept(t);
}; };
} }
public static <A, B> Consumer<A> link1(Function<A, B> function, Consumer<B> consumer) { @SafeVarargs
return a -> consumer.accept(function.apply(a)); public static <T extends Event> EventHandler<T> link(EventHandler<T>... handlers) {
} return t -> {
for (EventHandler<T> handler : handlers)
handler.handle(t);
};
}
public static <A, B, C> BiConsumer<A, C> link2(Function<A, B> function, BiConsumer<B, C> consumer) { public static <A, B> Consumer<A> link1(Function<A, B> function, Consumer<B> consumer) {
return (a, c) -> consumer.accept(function.apply(a), c); return a -> consumer.accept(function.apply(a));
} }
public static <A, B> Consumer<B> link2(Supplier<A> supplier, BiConsumer<A, B> consumer) { public static <A, B, C> BiConsumer<A, C> link2(Function<A, B> function, BiConsumer<B, C> consumer) {
return b -> consumer.accept(supplier.get(), b); return (a, c) -> consumer.accept(function.apply(a), c);
} }
public static <A, B> Supplier<B> link1(Supplier<A> supplier, Function<A, B> function) { public static <A, B> Consumer<B> link2(Supplier<A> supplier, BiConsumer<A, B> consumer) {
return () -> function.apply(supplier.get()); return b -> consumer.accept(supplier.get(), b);
} }
public static <A, B> Supplier<B> link1(Supplier<A> supplier, Function<A, B> function) {
return () -> function.apply(supplier.get());
}
} }

View File

@ -1,58 +1,58 @@
package moe.mickey.minecraft.skin.fx; package moe.mickey.minecraft.skin.fx;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
public class SkinAnimation { public class SkinAnimation {
protected int weight, left; protected int weight, left;
protected List<SkinTransition> transitions; protected List<SkinTransition> transitions;
@Deprecated @Deprecated
public SkinAnimation() { public SkinAnimation() {
this.transitions = new LinkedList<>(); this.transitions = new LinkedList<>();
} }
public SkinAnimation(int weight, SkinTransition... transitions) { public SkinAnimation(int weight, SkinTransition... transitions) {
this.weight = weight; this.weight = weight;
this.transitions = Arrays.asList(transitions); this.transitions = Arrays.asList(transitions);
init(); init();
} }
protected void init() { protected void init() {
transitions.forEach(t -> { transitions.forEach(t -> {
EventHandler<ActionEvent> oldHandler = t.getOnFinished(); EventHandler<ActionEvent> oldHandler = t.getOnFinished();
EventHandler<ActionEvent> newHandler = e -> left--; EventHandler<ActionEvent> newHandler = e -> left--;
newHandler = oldHandler == null ? newHandler : FunctionHelper.link(oldHandler, newHandler); newHandler = oldHandler == null ? newHandler : FunctionHelper.link(oldHandler, newHandler);
t.setOnFinished(newHandler); t.setOnFinished(newHandler);
}); });
} }
public int getWeight() { public int getWeight() {
return weight; return weight;
} }
public boolean isPlaying() { public boolean isPlaying() {
return left > 0; return left > 0;
} }
public void play() { public void play() {
transitions.forEach(SkinTransition::play); transitions.forEach(SkinTransition::play);
left = transitions.size(); left = transitions.size();
} }
public void playFromStart() { public void playFromStart() {
transitions.forEach(SkinTransition::playFromStart); transitions.forEach(SkinTransition::playFromStart);
left = transitions.size(); left = transitions.size();
} }
public void stop() { public void stop() {
transitions.forEach(SkinTransition::stop); transitions.forEach(SkinTransition::stop);
left = 0; left = 0;
} }
} }

View File

@ -1,93 +1,93 @@
package moe.mickey.minecraft.skin.fx; package moe.mickey.minecraft.skin.fx;
import javafx.animation.AnimationTimer;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Random; import java.util.Random;
import javafx.animation.AnimationTimer;
public class SkinAnimationPlayer { public class SkinAnimationPlayer {
protected final Random random = new Random(); protected final Random random = new Random();
protected LinkedList<SkinAnimation> animations = new LinkedList<>(); protected LinkedList<SkinAnimation> animations = new LinkedList<>();
protected SkinAnimation playing; protected SkinAnimation playing;
protected boolean running; protected boolean running;
protected int weightedSum = 0; protected int weightedSum = 0;
protected long lastPlayTime = -1L, interval = 10_000_000_000L; protected long lastPlayTime = -1L, interval = 10_000_000_000L;
protected AnimationTimer animationTimer = new AnimationTimer() { protected AnimationTimer animationTimer = new AnimationTimer() {
@Override @Override
public void handle(long now) { public void handle(long now) {
if (playing == null || !playing.isPlaying() && now - lastPlayTime > interval) { if (playing == null || !playing.isPlaying() && now - lastPlayTime > interval) {
int nextAni = random.nextInt(weightedSum); int nextAni = random.nextInt(weightedSum);
SkinAnimation tmp = null; SkinAnimation tmp = null;
for (SkinAnimation animation : animations) { for (SkinAnimation animation : animations) {
nextAni -= animation.getWeight(); nextAni -= animation.getWeight();
tmp = animation; tmp = animation;
if (nextAni <= 0) if (nextAni <= 0)
break; break;
} }
playing = tmp; playing = tmp;
if (playing == null && animations.size() > 0) if (playing == null && animations.size() > 0)
playing = animations.getLast(); playing = animations.getLast();
if (playing != null) { if (playing != null) {
playing.playFromStart(); playing.playFromStart();
lastPlayTime = now; lastPlayTime = now;
} }
} }
} }
}; };
public int getWeightedSum() { public int getWeightedSum() {
return weightedSum; return weightedSum;
} }
public void setInterval(long interval) { public void setInterval(long interval) {
this.interval = interval; this.interval = interval;
if (interval <1) if (interval < 1)
animationTimer.stop(); animationTimer.stop();
else else
start(); start();
} }
public long getInterval() { public long getInterval() {
return interval; return interval;
} }
public long getLastPlayTime() { public long getLastPlayTime() {
return lastPlayTime; return lastPlayTime;
} }
public boolean isRunning() { public boolean isRunning() {
return running; return running;
} }
public boolean isPlaying() { public boolean isPlaying() {
return playing != null; return playing != null;
} }
public SkinAnimation getPlaying() { public SkinAnimation getPlaying() {
return playing; return playing;
} }
public void addSkinAnimation(SkinAnimation... animations) { public void addSkinAnimation(SkinAnimation... animations) {
this.animations.addAll(Arrays.asList(animations)); this.animations.addAll(Arrays.asList(animations));
this.weightedSum = this.animations.stream().mapToInt(SkinAnimation::getWeight).sum(); this.weightedSum = this.animations.stream().mapToInt(SkinAnimation::getWeight).sum();
start(); start();
} }
public void start() { public void start() {
if (!running && weightedSum > 0 && interval > 0) { if (!running && weightedSum > 0 && interval > 0) {
animationTimer.start(); animationTimer.start();
running = true; running = true;
} }
} }
public void stop() { public void stop() {
if (running) if (running)
animationTimer.stop(); animationTimer.stop();
if (playing != null) if (playing != null)
playing.stop(); playing.stop();
running = false; running = false;
} }
} }

View File

@ -13,245 +13,243 @@ import javafx.scene.transform.Translate;
public class SkinCanvas extends Group { public class SkinCanvas extends Group {
public static final Image ALEX = new Image(SkinCanvas.class.getResourceAsStream("/assets/img/alex.png")); public static final Image ALEX = new Image(SkinCanvas.class.getResourceAsStream("/assets/img/alex.png"));
public static final Image STEVE = new Image(SkinCanvas.class.getResourceAsStream("/assets/img//steve.png")); public static final Image STEVE = new Image(SkinCanvas.class.getResourceAsStream("/assets/img//steve.png"));
public static final SkinCube ALEX_LARM = new SkinCube(3, 12, 4, 14F / 64F, 16F / 64F, 32F / 64F, 48F / 64F, 0F, true); public static final SkinCube ALEX_LARM = new SkinCube(3, 12, 4, 14F / 64F, 16F / 64F, 32F / 64F, 48F / 64F, 0F, true);
public static final SkinCube ALEX_RARM = new SkinCube(3, 12, 4, 14F / 64F, 16F / 64F, 40F / 64F, 16F / 64F, 0F, true); public static final SkinCube ALEX_RARM = new SkinCube(3, 12, 4, 14F / 64F, 16F / 64F, 40F / 64F, 16F / 64F, 0F, true);
public static final SkinCube STEVEN_LARM = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 32F / 64F, 48F / 64F, 0F, false); public static final SkinCube STEVEN_LARM = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 32F / 64F, 48F / 64F, 0F, false);
public static final SkinCube STEVEN_RARM = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 40F / 64F, 16F / 64F, 0F, false); public static final SkinCube STEVEN_RARM = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 40F / 64F, 16F / 64F, 0F, false);
protected Image srcSkin, skin; protected Image srcSkin, skin;
protected boolean isSlim; protected boolean isSlim;
protected double preW, preH; protected double preW, preH;
protected boolean msaa; protected boolean msaa;
protected SubScene subScene; protected SubScene subScene;
protected Group root = new Group(); protected Group root = new Group();
public final SkinMultipleCubes headOuter = new SkinMultipleCubes(8, 8, 8, 32F / 64F, 0F, 1.125, 0.2); public final SkinMultipleCubes headOuter = new SkinMultipleCubes(8, 8, 8, 32F / 64F, 0F, 1.125, 0.2);
public final SkinMultipleCubes bodyOuter = new SkinMultipleCubes(8, 12, 4, 16F / 64F, 32F / 64F, 1, 0.2); public final SkinMultipleCubes bodyOuter = new SkinMultipleCubes(8, 12, 4, 16F / 64F, 32F / 64F, 1, 0.2);
public final SkinMultipleCubes larmOuter = new SkinMultipleCubes(4, 12, 4, 48F / 64F, 48F / 64F, 1.0625, 0.2); public final SkinMultipleCubes larmOuter = new SkinMultipleCubes(4, 12, 4, 48F / 64F, 48F / 64F, 1.0625, 0.2);
public final SkinMultipleCubes rarmOuter = new SkinMultipleCubes(4, 12, 4, 40F / 64F, 32F / 64F, 1.0625, 0.2); public final SkinMultipleCubes rarmOuter = new SkinMultipleCubes(4, 12, 4, 40F / 64F, 32F / 64F, 1.0625, 0.2);
public final SkinMultipleCubes llegOuter = new SkinMultipleCubes(4, 12, 4, 0F / 64F, 48F / 64F, 1.0625, 0.2); public final SkinMultipleCubes llegOuter = new SkinMultipleCubes(4, 12, 4, 0F / 64F, 48F / 64F, 1.0625, 0.2);
public final SkinMultipleCubes rlegOuter = new SkinMultipleCubes(4, 12, 4, 0F / 64F, 32F / 64F, 1.0625, 0.2); public final SkinMultipleCubes rlegOuter = new SkinMultipleCubes(4, 12, 4, 0F / 64F, 32F / 64F, 1.0625, 0.2);
public final SkinCube headInside = new SkinCube(8, 8, 8, 32F / 64F, 16F / 64F, 0F, 0F, 0F, false); public final SkinCube headInside = new SkinCube(8, 8, 8, 32F / 64F, 16F / 64F, 0F, 0F, 0F, false);
public final SkinCube bodyInside = new SkinCube(8, 12, 4, 24F / 64F, 16F / 64F, 16F / 64F, 16F / 64F, 0.03F, false); public final SkinCube bodyInside = new SkinCube(8, 12, 4, 24F / 64F, 16F / 64F, 16F / 64F, 16F / 64F, 0.03F, false);
public final SkinCube larmInside = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 32F / 64F, 48F / 64F, 0F, false); public final SkinCube larmInside = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 32F / 64F, 48F / 64F, 0F, false);
public final SkinCube rarmInside = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 40F / 64F, 16F / 64F, 0F, false); public final SkinCube rarmInside = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 40F / 64F, 16F / 64F, 0F, false);
public final SkinCube llegInside = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 16F / 64F, 48F / 64F, 0F, false); public final SkinCube llegInside = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 16F / 64F, 48F / 64F, 0F, false);
public final SkinCube rlegInside = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 0F, 16F / 64F, 0F, false); public final SkinCube rlegInside = new SkinCube(4, 12, 4, 16F / 64F, 16F / 64F, 0F, 16F / 64F, 0F, false);
public final SkinGroup head = new SkinGroup( public final SkinGroup head = new SkinGroup(
new Rotate(0, 0, headInside.getHeight() / 2, 0, Rotate.X_AXIS), new Rotate(0, 0, headInside.getHeight() / 2, 0, Rotate.X_AXIS),
new Rotate(0, Rotate.Y_AXIS), new Rotate(0, Rotate.Y_AXIS),
new Rotate(0, 0, headInside.getHeight() / 2, 0, Rotate.Z_AXIS), new Rotate(0, 0, headInside.getHeight() / 2, 0, Rotate.Z_AXIS),
headOuter, headInside headOuter, headInside
); );
public final SkinGroup body = new SkinGroup( public final SkinGroup body = new SkinGroup(
new Rotate(0, Rotate.X_AXIS), new Rotate(0, Rotate.X_AXIS),
new Rotate(0, Rotate.Y_AXIS), new Rotate(0, Rotate.Y_AXIS),
new Rotate(0, Rotate.Z_AXIS), new Rotate(0, Rotate.Z_AXIS),
bodyOuter, bodyInside bodyOuter, bodyInside
); );
public final SkinGroup larm = new SkinGroup( public final SkinGroup larm = new SkinGroup(
new Rotate(0, 0, -larmInside.getHeight() / 2, 0, Rotate.X_AXIS), new Rotate(0, 0, -larmInside.getHeight() / 2, 0, Rotate.X_AXIS),
new Rotate(0, Rotate.Y_AXIS), new Rotate(0, Rotate.Y_AXIS),
new Rotate(0, +larmInside.getWidth() / 2, -larmInside.getHeight() / 2, 0, Rotate.Z_AXIS), new Rotate(0, +larmInside.getWidth() / 2, -larmInside.getHeight() / 2, 0, Rotate.Z_AXIS),
larmOuter, larmInside larmOuter, larmInside
); );
public final SkinGroup rarm = new SkinGroup( public final SkinGroup rarm = new SkinGroup(
new Rotate(0, 0, -rarmInside.getHeight() / 2, 0, Rotate.X_AXIS), new Rotate(0, 0, -rarmInside.getHeight() / 2, 0, Rotate.X_AXIS),
new Rotate(0, Rotate.Y_AXIS), new Rotate(0, Rotate.Y_AXIS),
new Rotate(0, -rarmInside.getWidth() / 2, -rarmInside.getHeight() / 2, 0, Rotate.Z_AXIS), new Rotate(0, -rarmInside.getWidth() / 2, -rarmInside.getHeight() / 2, 0, Rotate.Z_AXIS),
rarmOuter, rarmInside rarmOuter, rarmInside
); );
public final SkinGroup lleg = new SkinGroup( public final SkinGroup lleg = new SkinGroup(
new Rotate(0, 0, -llegInside.getHeight() / 2, 0, Rotate.X_AXIS), new Rotate(0, 0, -llegInside.getHeight() / 2, 0, Rotate.X_AXIS),
new Rotate(0, Rotate.Y_AXIS), new Rotate(0, Rotate.Y_AXIS),
new Rotate(0, 0, -llegInside.getHeight() / 2, 0, Rotate.Z_AXIS), new Rotate(0, 0, -llegInside.getHeight() / 2, 0, Rotate.Z_AXIS),
llegOuter, llegInside llegOuter, llegInside
); );
public final SkinGroup rleg = new SkinGroup( public final SkinGroup rleg = new SkinGroup(
new Rotate(0, 0, -rlegInside.getHeight() / 2, 0, Rotate.X_AXIS), new Rotate(0, 0, -rlegInside.getHeight() / 2, 0, Rotate.X_AXIS),
new Rotate(0, Rotate.Y_AXIS), new Rotate(0, Rotate.Y_AXIS),
new Rotate(0, 0, -rlegInside.getHeight() / 2, 0, Rotate.Z_AXIS), new Rotate(0, 0, -rlegInside.getHeight() / 2, 0, Rotate.Z_AXIS),
rlegOuter, rlegInside rlegOuter, rlegInside
); );
protected PerspectiveCamera camera = new PerspectiveCamera(true); protected PerspectiveCamera camera = new PerspectiveCamera(true);
protected Rotate xRotate = new Rotate(0, Rotate.X_AXIS); protected Rotate xRotate = new Rotate(0, Rotate.X_AXIS);
protected Rotate yRotate = new Rotate(180, Rotate.Y_AXIS); protected Rotate yRotate = new Rotate(180, Rotate.Y_AXIS);
protected Rotate zRotate = new Rotate(0, Rotate.Z_AXIS); protected Rotate zRotate = new Rotate(0, Rotate.Z_AXIS);
protected Translate translate = new Translate(0, 0, -80); protected Translate translate = new Translate(0, 0, -80);
protected Scale scale = new Scale(1, 1); protected Scale scale = new Scale(1, 1);
protected SkinAnimationPlayer animationplayer = new SkinAnimationPlayer(); protected SkinAnimationPlayer animationplayer = new SkinAnimationPlayer();
public SkinAnimationPlayer getAnimationplayer() { public SkinAnimationPlayer getAnimationplayer() {
return animationplayer; return animationplayer;
} }
public Image getSrcSkin() { public Image getSrcSkin() {
return srcSkin; return srcSkin;
} }
public Image getSkin() { public Image getSkin() {
return skin; return skin;
} }
public void updateSkin(Image skin, boolean isSlim) { public void updateSkin(Image skin, boolean isSlim) {
if (SkinHelper.isNoRequest(skin) && SkinHelper.isSkin(skin)) { if (SkinHelper.isNoRequest(skin) && SkinHelper.isSkin(skin)) {
this.srcSkin = skin; this.srcSkin = skin;
this.skin = SkinHelper.x32Tox64(skin); this.skin = SkinHelper.x32Tox64(skin);
int multiple = Math.max((int) (1024 / skin.getWidth()), 1); int multiple = Math.max((int) (1024 / skin.getWidth()), 1);
if (multiple > 1) if (multiple > 1)
this.skin = SkinHelper.enlarge(this.skin, multiple); this.skin = SkinHelper.enlarge(this.skin, multiple);
if (this.isSlim != isSlim) if (this.isSlim != isSlim)
updateSkinModel(isSlim); updateSkinModel(isSlim);
bindMaterial(root); bindMaterial(root);
} }
} }
protected void updateSkinModel(boolean isSlim) { protected void updateSkinModel(boolean isSlim) {
this.isSlim = isSlim; this.isSlim = isSlim;
FunctionHelper.alwaysB(SkinMultipleCubes::setWidth, isSlim ? 3 : 4, larmOuter, rarmOuter); FunctionHelper.alwaysB(SkinMultipleCubes::setWidth, isSlim ? 3 : 4, larmOuter, rarmOuter);
FunctionHelper.alwaysB(SkinCube::setWidth, isSlim ? 3D : 4D, larmInside, rarmInside); FunctionHelper.alwaysB(SkinCube::setWidth, isSlim ? 3D : 4D, larmInside, rarmInside);
FunctionHelper.alwaysB(Node::setTranslateX, -(bodyInside.getWidth() + larmInside.getWidth()) / 2, larm); FunctionHelper.alwaysB(Node::setTranslateX, -(bodyInside.getWidth() + larmInside.getWidth()) / 2, larm);
FunctionHelper.alwaysB(Node::setTranslateX, +(bodyInside.getWidth() + rarmInside.getWidth()) / 2, rarm); FunctionHelper.alwaysB(Node::setTranslateX, +(bodyInside.getWidth() + rarmInside.getWidth()) / 2, rarm);
if (isSlim) { if (isSlim) {
larmInside.setModel(ALEX_LARM.getModel()); larmInside.setModel(ALEX_LARM.getModel());
rarmInside.setModel(ALEX_RARM.getModel()); rarmInside.setModel(ALEX_RARM.getModel());
} else { } else {
larmInside.setModel(STEVEN_LARM.getModel()); larmInside.setModel(STEVEN_LARM.getModel());
rarmInside.setModel(STEVEN_RARM.getModel()); rarmInside.setModel(STEVEN_RARM.getModel());
} }
larm.getZRotate().setPivotX(-larmInside.getWidth() / 2); larm.getZRotate().setPivotX(-larmInside.getWidth() / 2);
rarm.getZRotate().setPivotX(+rarmInside.getWidth() / 2); rarm.getZRotate().setPivotX(+rarmInside.getWidth() / 2);
} }
public SkinCanvas(double preW, double preH) { public SkinCanvas(double preW, double preH) {
this(STEVE, preW, preH, true); this(STEVE, preW, preH, true);
} }
public SkinCanvas(Image skin, double preW, double preH, boolean msaa) { public SkinCanvas(Image skin, double preW, double preH, boolean msaa) {
this.skin = skin; this.skin = skin;
this.preW = preW; this.preW = preW;
this.preH = preH; this.preH = preH;
this.msaa = msaa; this.msaa = msaa;
init(); init();
} }
protected Material createMaterial() { protected Material createMaterial() {
PhongMaterial material = new PhongMaterial(); PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(skin); material.setDiffuseMap(skin);
return material; return material;
} }
protected void bindMaterial(Group group) { protected void bindMaterial(Group group) {
Material material = createMaterial(); Material material = createMaterial();
for (Node node : group.getChildren()) for (Node node : group.getChildren())
if (node instanceof Shape3D) if (node instanceof Shape3D)
((Shape3D) node).setMaterial(material); ((Shape3D) node).setMaterial(material);
else if (node instanceof SkinMultipleCubes) else if (node instanceof SkinMultipleCubes)
((SkinMultipleCubes) node).updateSkin(skin); ((SkinMultipleCubes) node).updateSkin(skin);
else if (node instanceof Group) else if (node instanceof Group)
bindMaterial((Group) node); bindMaterial((Group) node);
} }
protected Group createPlayerModel() {
head.setTranslateY(-(bodyInside.getHeight() + headInside.getHeight()) / 2);
larm.setTranslateX(-(bodyInside.getWidth() + larmInside.getWidth()) / 2);
rarm.setTranslateX(+(bodyInside.getWidth() + rarmInside.getWidth()) / 2);
protected Group createPlayerModel() { lleg.setTranslateX(-(bodyInside.getWidth() - llegInside.getWidth()) / 2);
head.setTranslateY(-(bodyInside.getHeight() + headInside.getHeight()) / 2); rleg.setTranslateX(+(bodyInside.getWidth() - rlegInside.getWidth()) / 2);
larm.setTranslateX(-(bodyInside.getWidth() + larmInside.getWidth()) / 2); lleg.setTranslateY(+(bodyInside.getHeight() + llegInside.getHeight()) / 2);
rarm.setTranslateX(+(bodyInside.getWidth() + rarmInside.getWidth()) / 2); rleg.setTranslateY(+(bodyInside.getHeight() + rlegInside.getHeight()) / 2);
lleg.setTranslateX(-(bodyInside.getWidth() - llegInside.getWidth()) / 2); root.getTransforms().addAll(xRotate);
rleg.setTranslateX(+(bodyInside.getWidth() - rlegInside.getWidth()) / 2);
lleg.setTranslateY(+(bodyInside.getHeight() + llegInside.getHeight()) / 2); root.getChildren().addAll(
rleg.setTranslateY(+(bodyInside.getHeight() + rlegInside.getHeight()) / 2); head,
body,
larm,
rarm,
lleg,
rleg
);
updateSkin(skin, false);
root.getTransforms().addAll(xRotate); return root;
}
root.getChildren().addAll( protected SubScene createSubScene() {
head, Group group = new Group();
body, group.getChildren().add(createPlayerModel());
larm, group.getTransforms().add(zRotate);
rarm,
lleg,
rleg
);
updateSkin(skin, false);
return root; camera.getTransforms().addAll(yRotate, translate, scale);
}
protected SubScene createSubScene() { subScene = new SubScene(group, preW, preH, true,
Group group = new Group(); msaa ? SceneAntialiasing.BALANCED : SceneAntialiasing.DISABLED);
group.getChildren().add(createPlayerModel());
group.getTransforms().add(zRotate);
camera.getTransforms().addAll(yRotate, translate, scale);
subScene = new SubScene(group, preW, preH, true,
msaa ? SceneAntialiasing.BALANCED : SceneAntialiasing.DISABLED);
subScene.setCamera(camera); subScene.setCamera(camera);
return subScene; return subScene;
} }
protected void init() { protected void init() {
getChildren().add(createSubScene()); getChildren().add(createSubScene());
} }
private double lastX, lastY; private double lastX, lastY;
public void enableRotation(double sensitivity) { public void enableRotation(double sensitivity) {
addEventHandler(MouseEvent.MOUSE_PRESSED, e -> { addEventHandler(MouseEvent.MOUSE_PRESSED, e -> {
lastX = -1; lastX = -1;
lastY = -1; lastY = -1;
}); });
addEventHandler(MouseEvent.MOUSE_DRAGGED, e -> { addEventHandler(MouseEvent.MOUSE_DRAGGED, e -> {
if (!(lastX == -1 || lastY == -1)) { if (!(lastX == -1 || lastY == -1)) {
if (e.isAltDown() || e.isControlDown() || e.isShiftDown()) { if (e.isAltDown() || e.isControlDown() || e.isShiftDown()) {
if (e.isShiftDown()) if (e.isShiftDown())
zRotate.setAngle(zRotate.getAngle() - (e.getSceneY() - lastY) * sensitivity); zRotate.setAngle(zRotate.getAngle() - (e.getSceneY() - lastY) * sensitivity);
if (e.isAltDown()) if (e.isAltDown())
yRotate.setAngle(yRotate.getAngle() + (e.getSceneX() - lastX) * sensitivity); yRotate.setAngle(yRotate.getAngle() + (e.getSceneX() - lastX) * sensitivity);
if (e.isControlDown()) if (e.isControlDown())
xRotate.setAngle(xRotate.getAngle() + (e.getSceneY() - lastY) * sensitivity); xRotate.setAngle(xRotate.getAngle() + (e.getSceneY() - lastY) * sensitivity);
} else { } else {
double yaw = yRotate.getAngle() + (e.getSceneX() - lastX) * sensitivity; double yaw = yRotate.getAngle() + (e.getSceneX() - lastX) * sensitivity;
yaw %= 360; yaw %= 360;
if (yaw < 0) if (yaw < 0)
yaw += 360; yaw += 360;
int flagX = yaw < 90 || yaw > 270 ? 1 : -1; int flagX = yaw < 90 || yaw > 270 ? 1 : -1;
int flagZ = yaw < 180 ? -1 : 1; int flagZ = yaw < 180 ? -1 : 1;
double kx = Math.abs(90 - yaw % 180) / 90 * flagX, kz = Math.abs(90 - (yaw + 90) % 180) / 90 * flagZ; double kx = Math.abs(90 - yaw % 180) / 90 * flagX, kz = Math.abs(90 - (yaw + 90) % 180) / 90 * flagZ;
xRotate.setAngle(xRotate.getAngle() + (e.getSceneY() - lastY) * sensitivity * kx); xRotate.setAngle(xRotate.getAngle() + (e.getSceneY() - lastY) * sensitivity * kx);
yRotate.setAngle(yaw); yRotate.setAngle(yaw);
zRotate.setAngle(zRotate.getAngle() + (e.getSceneY() - lastY) * sensitivity * kz); zRotate.setAngle(zRotate.getAngle() + (e.getSceneY() - lastY) * sensitivity * kz);
} }
} }
lastX = e.getSceneX(); lastX = e.getSceneX();
lastY = e.getSceneY(); lastY = e.getSceneY();
}); });
addEventHandler(ScrollEvent.SCROLL, e -> { addEventHandler(ScrollEvent.SCROLL, e -> {
double delta = (e.getDeltaY() > 0 ? 1 : e.getDeltaY() == 0 ? 0 : -1) / 10D * sensitivity; double delta = (e.getDeltaY() > 0 ? 1 : e.getDeltaY() == 0 ? 0 : -1) / 10D * sensitivity;
scale.setX(Math.min(Math.max(scale.getX() - delta, 0.1), 10)); scale.setX(Math.min(Math.max(scale.getX() - delta, 0.1), 10));
scale.setY(Math.min(Math.max(scale.getY() - delta, 0.1), 10)); scale.setY(Math.min(Math.max(scale.getY() - delta, 0.1), 10));
}); });
} }
} }

View File

@ -8,135 +8,135 @@ import org.jackhuang.hmcl.util.ArrayUtils;
public class SkinCube extends MeshView { public class SkinCube extends MeshView {
public static class Model extends TriangleMesh { public static class Model extends TriangleMesh {
public Model(float width, float height, float depth, float scaleX, float scaleY, float startX, float startY, boolean isSlim) { public Model(float width, float height, float depth, float scaleX, float scaleY, float startX, float startY, boolean isSlim) {
getPoints().addAll(createPoints(width, height, depth)); getPoints().addAll(createPoints(width, height, depth));
getTexCoords().addAll(createTexCoords(width, height, depth, scaleX, scaleY, startX, startY, isSlim)); getTexCoords().addAll(createTexCoords(width, height, depth, scaleX, scaleY, startX, startY, isSlim));
getFaces().addAll(createFaces()); getFaces().addAll(createFaces());
} }
public static float[] createPoints(float width, float height, float depth) { public static float[] createPoints(float width, float height, float depth) {
width /= 2F; width /= 2F;
height /= 2F; height /= 2F;
depth /= 2F; depth /= 2F;
return new float[]{ return new float[]{
-width, -height, depth, // P0 -width, -height, depth, // P0
width, -height, depth, // P1 width, -height, depth, // P1
-width, height, depth, // P2 -width, height, depth, // P2
width, height, depth, // P3 width, height, depth, // P3
-width, -height, -depth, // P4 -width, -height, -depth, // P4
width, -height, -depth, // P5 width, -height, -depth, // P5
-width, height, -depth, // P6 -width, height, -depth, // P6
width, height, -depth // P7 width, height, -depth // P7
}; };
} }
public static float[] createTexCoords(float width, float height, float depth, float scaleX, float scaleY, public static float[] createTexCoords(float width, float height, float depth, float scaleX, float scaleY,
float startX, float startY, boolean isSlim) { float startX, float startY, boolean isSlim) {
float x = (width + depth) * 2, y = height + depth, half_width = width / x * scaleX, half_depth = depth / x * scaleX, float x = (width + depth) * 2, y = height + depth, half_width = width / x * scaleX, half_depth = depth / x * scaleX,
top_x = depth / x * scaleX + startX, top_y = startY, arm4 = isSlim ? half_depth : half_width, top_x = depth / x * scaleX + startX, top_y = startY, arm4 = isSlim ? half_depth : half_width,
bottom_x = startX, middle_y = depth / y * scaleY + top_y, bottom_y = scaleY + top_y; bottom_x = startX, middle_y = depth / y * scaleY + top_y, bottom_y = scaleY + top_y;
return new float[]{ return new float[]{
top_x, top_y, // T0 --- top_x, top_y, // T0 ---
top_x + half_width, top_y, // T1 | top_x + half_width, top_y, // T1 |
top_x + half_width * 2, top_y, // T2 --- top_x + half_width * 2, top_y, // T2 ---
bottom_x, middle_y, // T3 --- bottom_x, middle_y, // T3 ---
bottom_x + half_depth, middle_y, // T4 | bottom_x + half_depth, middle_y, // T4 |
bottom_x + half_depth + half_width, middle_y, // T5 | bottom_x + half_depth + half_width, middle_y, // T5 |
bottom_x + scaleX - arm4, middle_y, // T6 | bottom_x + scaleX - arm4, middle_y, // T6 |
bottom_x + scaleX, middle_y, // T7 --- bottom_x + scaleX, middle_y, // T7 ---
bottom_x, bottom_y, // T8 --- bottom_x, bottom_y, // T8 ---
bottom_x + half_depth, bottom_y, // T9 | bottom_x + half_depth, bottom_y, // T9 |
bottom_x + half_depth + half_width, bottom_y, // T10 | bottom_x + half_depth + half_width, bottom_y, // T10 |
bottom_x + scaleX - arm4, bottom_y, // T11 | bottom_x + scaleX - arm4, bottom_y, // T11 |
bottom_x + scaleX, bottom_y // T12 --- bottom_x + scaleX, bottom_y // T12 ---
}; };
} }
public static int[] createFaces() { public static int[] createFaces() {
int faces[] = new int[]{ int[] faces = new int[]{
// TOP // TOP
5, 0, 4, 1, 0, 5, //P5,T0, P4,T1, P0,T5 5, 0, 4, 1, 0, 5, //P5,T0, P4,T1, P0,T5
5, 0, 0, 5, 1, 4, //P5,T0, P0,T5, P1,T4 5, 0, 0, 5, 1, 4, //P5,T0, P0,T5, P1,T4
// RIGHT // RIGHT
0, 5, 4, 6, 6, 11, //P0,T4 ,P4,T3, P6,T8 0, 5, 4, 6, 6, 11, //P0,T4 ,P4,T3, P6,T8
0, 5, 6, 11, 2, 10, //P0,T4 ,P6,T8, P2,T9 0, 5, 6, 11, 2, 10, //P0,T4 ,P6,T8, P2,T9
// FRONT // FRONT
1, 4, 0, 5, 2, 10, //P1,T5, P0,T4, P2,T9 1, 4, 0, 5, 2, 10, //P1,T5, P0,T4, P2,T9
1, 4, 2, 10, 3, 9, //P1,T5, P2,T9, P3,T10 1, 4, 2, 10, 3, 9, //P1,T5, P2,T9, P3,T10
// LEFT // LEFT
5, 3, 1, 4, 3, 9, //P5,T6, P1,T5, P3,T10 5, 3, 1, 4, 3, 9, //P5,T6, P1,T5, P3,T10
5, 3, 3, 9, 7, 8, //P5,T6, P3,T10,P7,T11 5, 3, 3, 9, 7, 8, //P5,T6, P3,T10,P7,T11
// BACK // BACK
4, 6, 5, 7, 7, 12, //P4,T6, P5,T7, P7,T12 4, 6, 5, 7, 7, 12, //P4,T6, P5,T7, P7,T12
4, 6, 7, 12, 6, 11, //P4,T6, P7,T12,P6,T11 4, 6, 7, 12, 6, 11, //P4,T6, P7,T12,P6,T11
// BOTTOM // BOTTOM
3, 5, 2, 6, 6, 2, //P3,T2, P2,T1, P6,T5 3, 5, 2, 6, 6, 2, //P3,T2, P2,T1, P6,T5
3, 5, 6, 2, 7, 1 //P3,T2, P6,T5, P7,T6 3, 5, 6, 2, 7, 1 //P3,T2, P6,T5, P7,T6
}; };
int[] copy = faces.clone(); int[] copy = faces.clone();
ArrayUtils.reverse(copy); ArrayUtils.reverse(copy);
for (int i = 0; i < copy.length; i += 2) { for (int i = 0; i < copy.length; i += 2) {
int tmp = copy[i]; int tmp = copy[i];
copy[i] = copy[i + 1]; copy[i] = copy[i + 1];
copy[i + 1] = tmp; copy[i + 1] = tmp;
} }
return ArrayUtils.addAll(faces, copy); return ArrayUtils.addAll(faces, copy);
} }
} }
private double width, height, depth; private double width, height, depth;
private boolean isSlim; private boolean isSlim;
private Image skin; private Image skin;
private Mesh model; private Mesh model;
public SkinCube(float width, float height, float depth, float scaleX, float scaleY, float startX, float startY, float enlarge, boolean isSlim) { public SkinCube(float width, float height, float depth, float scaleX, float scaleY, float startX, float startY, float enlarge, boolean isSlim) {
this.width = width; this.width = width;
this.height = height; this.height = height;
this.depth = depth; this.depth = depth;
this.isSlim = isSlim; this.isSlim = isSlim;
setMesh(model = new Model(width + enlarge, height + enlarge, depth + enlarge, scaleX, scaleY, startX, startY, isSlim)); setMesh(model = new Model(width + enlarge, height + enlarge, depth + enlarge, scaleX, scaleY, startX, startY, isSlim));
} }
public void setWidth(double width) { public void setWidth(double width) {
this.width = width; this.width = width;
} }
public double getWidth() { public double getWidth() {
return width; return width;
} }
public void setHeight(double height) { public void setHeight(double height) {
this.height = height; this.height = height;
} }
public double getHeight() { public double getHeight() {
return height; return height;
} }
public void setDepth(double depth) { public void setDepth(double depth) {
this.depth = depth; this.depth = depth;
} }
public double getDepth() { public double getDepth() {
return depth; return depth;
} }
public boolean isSlim() { public boolean isSlim() {
return isSlim; return isSlim;
} }
public Mesh getModel() { public Mesh getModel() {
return model; return model;
} }
public void setModel(Mesh model) { public void setModel(Mesh model) {
this.model = model; this.model = model;
setMesh(model); setMesh(model);
} }
} }

View File

@ -6,40 +6,40 @@ import javafx.scene.transform.Rotate;
public class SkinGroup extends Group { public class SkinGroup extends Group {
protected Rotate xRotate, yRotate, zRotate; protected Rotate xRotate, yRotate, zRotate;
public SkinGroup(Rotate xRotate, Rotate yRotate, Rotate zRotate, Node... nodes) { public SkinGroup(Rotate xRotate, Rotate yRotate, Rotate zRotate, Node... nodes) {
this.xRotate = xRotate; this.xRotate = xRotate;
this.yRotate = yRotate; this.yRotate = yRotate;
this.zRotate = zRotate; this.zRotate = zRotate;
Group group = new Group(); Group group = new Group();
group.getChildren().addAll(nodes); group.getChildren().addAll(nodes);
getChildren().add(addRotate(group, xRotate, yRotate, zRotate)); getChildren().add(addRotate(group, xRotate, yRotate, zRotate));
} }
protected Group addRotate(Group group, Rotate... rotates) { protected Group addRotate(Group group, Rotate... rotates) {
for (Rotate rotate : rotates) for (Rotate rotate : rotates)
group = addRotate(group, rotate); group = addRotate(group, rotate);
return group; return group;
} }
protected Group addRotate(Group group, Rotate rotate) { protected Group addRotate(Group group, Rotate rotate) {
Group newGroup = new Group(); Group newGroup = new Group();
group.getTransforms().add(rotate); group.getTransforms().add(rotate);
newGroup.getChildren().add(group); newGroup.getChildren().add(group);
return newGroup; return newGroup;
} }
public Rotate getXRotate() { public Rotate getXRotate() {
return xRotate; return xRotate;
} }
public Rotate getYRotate() { public Rotate getYRotate() {
return yRotate; return yRotate;
} }
public Rotate getZRotate() { public Rotate getZRotate() {
return zRotate; return zRotate;
} }
} }

View File

@ -1,138 +1,140 @@
package moe.mickey.minecraft.skin.fx; package moe.mickey.minecraft.skin.fx;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javafx.embed.swing.SwingFXUtils; import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.PixelReader; import javafx.scene.image.PixelReader;
import javafx.scene.image.PixelWriter; import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage; import javafx.scene.image.WritableImage;
public interface SkinHelper { import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public static class PixelCopyer { public final class SkinHelper {
protected Image srcImage; private SkinHelper() {
protected WritableImage newImage; }
public PixelCopyer(Image srcImage, WritableImage newImage) { public static class PixelCopyer {
this.srcImage = srcImage;
this.newImage = newImage;
}
public void copy(int srcX, int srcY, int width, int height) { protected Image srcImage;
copy(srcX, srcY, srcX, srcY, width, height); protected WritableImage newImage;
}
public void copy(int srcX, int srcY, int toX, int toY, int width, int height) { public PixelCopyer(Image srcImage, WritableImage newImage) {
copy(srcX, srcY, toX, toY, width, height, false, false); this.srcImage = srcImage;
} this.newImage = newImage;
}
public void copy(int srcX, int srcY, int toX, int toY, int width, int height, boolean reversalX, boolean reversalY) { public void copy(int srcX, int srcY, int width, int height) {
PixelReader reader = srcImage.getPixelReader(); copy(srcX, srcY, srcX, srcY, width, height);
PixelWriter writer = newImage.getPixelWriter(); }
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
writer.setArgb(toX + x, toY + y,
reader.getArgb(srcX + (reversalX ? width - x - 1 : x), srcY + (reversalY ? height - y - 1 : y)));
}
public void copy(float srcX, float srcY, float toX, float toY, float width, float height) { public void copy(int srcX, int srcY, int toX, int toY, int width, int height) {
copy(srcX, srcY, toX, toY, width, height, false, false); copy(srcX, srcY, toX, toY, width, height, false, false);
} }
public void copy(float srcX, float srcY, float toX, float toY, float width, float height, boolean reversalX, boolean reversalY) { public void copy(int srcX, int srcY, int toX, int toY, int width, int height, boolean reversalX, boolean reversalY) {
PixelReader reader = srcImage.getPixelReader(); PixelReader reader = srcImage.getPixelReader();
PixelWriter writer = newImage.getPixelWriter(); PixelWriter writer = newImage.getPixelWriter();
int srcScaleX = (int) srcImage.getWidth(); for (int x = 0; x < width; x++)
int srcScaleY = (int) srcImage.getHeight(); for (int y = 0; y < height; y++)
int newScaleX = (int) newImage.getWidth(); writer.setArgb(toX + x, toY + y,
int newScaleY = (int) newImage.getHeight(); reader.getArgb(srcX + (reversalX ? width - x - 1 : x), srcY + (reversalY ? height - y - 1 : y)));
int srcWidth = (int) (width * srcScaleX); }
int srcHeight = (int) (height * srcScaleY);
for (int x = 0; x < srcWidth; x++) public void copy(float srcX, float srcY, float toX, float toY, float width, float height) {
for (int y = 0; y < srcHeight; y++) copy(srcX, srcY, toX, toY, width, height, false, false);
writer.setArgb((int) (toX * newScaleX + x), (int) (toY * newScaleY + y), }
reader.getArgb((int) (srcX * srcScaleX + (reversalX ? srcWidth - x - 1 : x)),
(int) (srcY * srcScaleY + (reversalY ? srcHeight - y - 1 : y))));
}
} public void copy(float srcX, float srcY, float toX, float toY, float width, float height, boolean reversalX, boolean reversalY) {
PixelReader reader = srcImage.getPixelReader();
PixelWriter writer = newImage.getPixelWriter();
int srcScaleX = (int) srcImage.getWidth();
int srcScaleY = (int) srcImage.getHeight();
int newScaleX = (int) newImage.getWidth();
int newScaleY = (int) newImage.getHeight();
int srcWidth = (int) (width * srcScaleX);
int srcHeight = (int) (height * srcScaleY);
public static boolean isNoRequest(Image image) { for (int x = 0; x < srcWidth; x++)
return image.getRequestedWidth() == 0 && image.getRequestedHeight() == 0; for (int y = 0; y < srcHeight; y++)
} writer.setArgb((int) (toX * newScaleX + x), (int) (toY * newScaleY + y),
reader.getArgb((int) (srcX * srcScaleX + (reversalX ? srcWidth - x - 1 : x)),
(int) (srcY * srcScaleY + (reversalY ? srcHeight - y - 1 : y))));
}
public static boolean isSkin(Image image) { }
return image.getWidth() % 64 == 0 && image.getWidth() / 64 > 0 &&
(image.getHeight() == image.getWidth() / 2 || image.getHeight() == image.getWidth());
}
public static Image x32Tox64(Image srcSkin) { public static boolean isNoRequest(Image image) {
if (srcSkin.getHeight() == 64) return image.getRequestedWidth() == 0 && image.getRequestedHeight() == 0;
return srcSkin; }
WritableImage newSkin = new WritableImage((int) srcSkin.getWidth(), (int) srcSkin.getHeight() * 2); public static boolean isSkin(Image image) {
PixelCopyer copyer = new PixelCopyer(srcSkin, newSkin); return image.getWidth() % 64 == 0 && image.getWidth() / 64 > 0 &&
// HEAD & HAT (image.getHeight() == image.getWidth() / 2 || image.getHeight() == image.getWidth());
copyer.copy(0 / 64F, 0 / 32F, 0 / 64F, 0 / 64F, 64 / 64F, 16 / 32F); }
// LEFT-LEG
x32Tox64(copyer, 0 / 64F, 16 / 32F, 16 / 64F, 48 / 64F, 4 / 64F, 12 / 32F, 4 / 64F);
// RIGHT-LEG
copyer.copy(0 / 64F, 16 / 32F, 0 / 64F, 16 / 64F, 16 / 64F, 16 / 32F);
// BODY
copyer.copy(16 / 64F, 16 / 32F, 16 / 64F, 16 / 64F, 24 / 64F, 16 / 32F);
// LEFT-ARM
x32Tox64(copyer, 40 / 64F, 16 / 32F, 32 / 64F, 48 / 64F, 4 / 64F, 12 / 32F, 4 / 64F);
// RIGHT-ARM
copyer.copy(40 / 64F, 16 / 32F, 40 / 64F, 16 / 64F, 16 / 64F, 16 / 32F);
return newSkin; public static Image x32Tox64(Image srcSkin) {
} if (srcSkin.getHeight() == 64)
return srcSkin;
static void x32Tox64(PixelCopyer copyer, float srcX, float srcY, float toX, float toY, float width, float height, float depth) { WritableImage newSkin = new WritableImage((int) srcSkin.getWidth(), (int) srcSkin.getHeight() * 2);
// TOP PixelCopyer copyer = new PixelCopyer(srcSkin, newSkin);
copyer.copy(srcX + depth, srcY, toX + depth, toY, width, depth * 2, true, false); // HEAD & HAT
// BOTTOM copyer.copy(0 / 64F, 0 / 32F, 0 / 64F, 0 / 64F, 64 / 64F, 16 / 32F);
copyer.copy(srcX + depth + width, srcY, toX + depth + width, toY, width, depth * 2, true, false); // LEFT-LEG
// INS x32Tox64(copyer, 0 / 64F, 16 / 32F, 16 / 64F, 48 / 64F, 4 / 64F, 12 / 32F, 4 / 64F);
copyer.copy(srcX, srcY + depth * 2, toX + width + depth, toY + depth, depth, height, true, false); // RIGHT-LEG
// OUTS copyer.copy(0 / 64F, 16 / 32F, 0 / 64F, 16 / 64F, 16 / 64F, 16 / 32F);
copyer.copy(srcX + width + depth, srcY + depth * 2, toX, toY + depth, depth, height, true, false); // BODY
// FRONT copyer.copy(16 / 64F, 16 / 32F, 16 / 64F, 16 / 64F, 24 / 64F, 16 / 32F);
copyer.copy(srcX + depth, srcY + depth * 2, toX + depth, toY + depth, width, height, true, false); // LEFT-ARM
// BACK x32Tox64(copyer, 40 / 64F, 16 / 32F, 32 / 64F, 48 / 64F, 4 / 64F, 12 / 32F, 4 / 64F);
copyer.copy(srcX + width + depth * 2, srcY + depth * 2, toX + width + depth * 2, toY + depth, width, height, true, false); // RIGHT-ARM
} copyer.copy(40 / 64F, 16 / 32F, 40 / 64F, 16 / 64F, 16 / 64F, 16 / 32F);
public static Image enlarge(Image srcSkin, int multiple) { return newSkin;
WritableImage newSkin = new WritableImage((int) srcSkin.getWidth() * multiple, (int) srcSkin.getHeight() * multiple); }
PixelReader reader = srcSkin.getPixelReader();
PixelWriter writer = newSkin.getPixelWriter();
for (int x = 0, lenX = (int) srcSkin.getWidth(); x < lenX; x++) static void x32Tox64(PixelCopyer copyer, float srcX, float srcY, float toX, float toY, float width, float height, float depth) {
for (int y = 0, lenY = (int) srcSkin.getHeight(); y < lenY; y++) // TOP
for (int mx = 0; mx < multiple; mx++) copyer.copy(srcX + depth, srcY, toX + depth, toY, width, depth * 2, true, false);
for (int my = 0; my < multiple; my++) { // BOTTOM
int argb = reader.getArgb(x, y); copyer.copy(srcX + depth + width, srcY, toX + depth + width, toY, width, depth * 2, true, false);
writer.setArgb(x * multiple + mx, y * multiple + my, argb); // INS
} copyer.copy(srcX, srcY + depth * 2, toX + width + depth, toY + depth, depth, height, true, false);
// OUTS
copyer.copy(srcX + width + depth, srcY + depth * 2, toX, toY + depth, depth, height, true, false);
// FRONT
copyer.copy(srcX + depth, srcY + depth * 2, toX + depth, toY + depth, width, height, true, false);
// BACK
copyer.copy(srcX + width + depth * 2, srcY + depth * 2, toX + width + depth * 2, toY + depth, width, height, true, false);
}
return newSkin; public static Image enlarge(Image srcSkin, int multiple) {
} WritableImage newSkin = new WritableImage((int) srcSkin.getWidth() * multiple, (int) srcSkin.getHeight() * multiple);
PixelReader reader = srcSkin.getPixelReader();
PixelWriter writer = newSkin.getPixelWriter();
public static void saveToFile(Image image, File output) { for (int x = 0, lenX = (int) srcSkin.getWidth(); x < lenX; x++)
BufferedImage buffer = SwingFXUtils.fromFXImage(image, null); for (int y = 0, lenY = (int) srcSkin.getHeight(); y < lenY; y++)
try { for (int mx = 0; mx < multiple; mx++)
ImageIO.write(buffer, "png", output); for (int my = 0; my < multiple; my++) {
} catch (IOException e) { int argb = reader.getArgb(x, y);
throw new RuntimeException(e); writer.setArgb(x * multiple + mx, y * multiple + my, argb);
} }
}
return newSkin;
}
public static void saveToFile(Image image, File output) {
BufferedImage buffer = SwingFXUtils.fromFXImage(image, null);
try {
ImageIO.write(buffer, "png", output);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
} }

View File

@ -1,8 +1,5 @@
package moe.mickey.minecraft.skin.fx; package moe.mickey.minecraft.skin.fx;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import javafx.geometry.Point2D; import javafx.geometry.Point2D;
import javafx.scene.Group; import javafx.scene.Group;
import javafx.scene.image.Image; import javafx.scene.image.Image;
@ -12,150 +9,153 @@ import javafx.scene.paint.Material;
import javafx.scene.paint.PhongMaterial; import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box; import javafx.scene.shape.Box;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
public class SkinMultipleCubes extends Group { public class SkinMultipleCubes extends Group {
public static class Face extends Group { public static class Face extends Group {
public Face(Image image, int startX, int startY, int width, int height, int interval, boolean reverseX, boolean reverseY, public Face(Image image, int startX, int startY, int width, int height, int interval, boolean reverseX, boolean reverseY,
Supplier<Box> supplier, BiConsumer<Box, Point2D> consumer) { Supplier<Box> supplier, BiConsumer<Box, Point2D> consumer) {
PixelReader reader = image.getPixelReader(); PixelReader reader = image.getPixelReader();
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
int argb; int argb;
if ((argb = reader.getArgb(startX + (reverseX ? width - x - 1 : x) * interval, if ((argb = reader.getArgb(startX + (reverseX ? width - x - 1 : x) * interval,
startY + (reverseY ? height - y - 1 : y) * interval)) != 0) { startY + (reverseY ? height - y - 1 : y) * interval)) != 0) {
Box pixel = supplier.get(); Box pixel = supplier.get();
consumer.accept(pixel, new Point2D(x, y)); consumer.accept(pixel, new Point2D(x, y));
pixel.setMaterial(createMaterial(Color.rgb( pixel.setMaterial(createMaterial(Color.rgb(
(argb >> 16) & 0xFF, (argb >> 8) & 0xFF, (argb >> 0) & 0xFF))); (argb >> 16) & 0xFF, (argb >> 8) & 0xFF, (argb >> 0) & 0xFF)));
getChildren().add(pixel); getChildren().add(pixel);
} }
} }
} }
protected Material createMaterial(Color color) { protected Material createMaterial(Color color) {
return new PhongMaterial(color); return new PhongMaterial(color);
} }
} }
protected int width, height, depth; protected int width, height, depth;
protected float startX, startY; protected float startX, startY;
protected double length, thick; protected double length, thick;
public SkinMultipleCubes(int width, int height, int depth, float startX, float startY, double length, double thick) { public SkinMultipleCubes(int width, int height, int depth, float startX, float startY, double length, double thick) {
this.width = width; this.width = width;
this.height = height; this.height = height;
this.depth = depth; this.depth = depth;
this.startX = startX; this.startX = startX;
this.startY = startY; this.startY = startY;
this.length = length; this.length = length;
this.thick = thick; this.thick = thick;
} }
public void setWidth(int width) { public void setWidth(int width) {
this.width = width; this.width = width;
} }
public int getWidth() { public int getWidth() {
return width; return width;
} }
public void setHeight(int height) { public void setHeight(int height) {
this.height = height; this.height = height;
} }
public int getHeight() { public int getHeight() {
return height; return height;
} }
public void setDepth(int depth) { public void setDepth(int depth) {
this.depth = depth; this.depth = depth;
} }
public int getDepth() { public int getDepth() {
return depth; return depth;
} }
public void setStartX(float startX) { public void setStartX(float startX) {
this.startX = startX; this.startX = startX;
} }
public float getStartX() { public float getStartX() {
return startX; return startX;
} }
public void setStartY(float startY) { public void setStartY(float startY) {
this.startY = startY; this.startY = startY;
} }
public float getStartY() { public float getStartY() {
return startY; return startY;
} }
public void setLength(double length) { public void setLength(double length) {
this.length = length; this.length = length;
} }
public double getLength() { public double getLength() {
return length; return length;
} }
public void setThick(double thick) { public void setThick(double thick) {
this.thick = thick; this.thick = thick;
} }
public double getThick() { public double getThick() {
return thick; return thick;
} }
public void updateSkin(Image skin) { public void updateSkin(Image skin) {
getChildren().clear(); getChildren().clear();
int start_x = (int) (startX * skin.getWidth()), start_y = (int) (startY * skin.getHeight()), int start_x = (int) (startX * skin.getWidth()), start_y = (int) (startY * skin.getHeight()),
interval = (int) Math.max(skin.getWidth() / 64, 1), interval = (int) Math.max(skin.getWidth() / 64, 1),
width_interval = width * interval, height_interval = height * interval, depth_interval = depth * interval; width_interval = width * interval, height_interval = height * interval, depth_interval = depth * interval;
// FRONT // FRONT
getChildren().add(new Face(skin, start_x + depth_interval, start_y + depth_interval, width, height, interval, false, false, getChildren().add(new Face(skin, start_x + depth_interval, start_y + depth_interval, width, height, interval, false, false,
() -> new Box(length, length, thick), (b, p) -> { () -> new Box(length, length, thick), (b, p) -> {
b.setTranslateX(((width - 1) / 2.0 - p.getX()) * b.getWidth()); b.setTranslateX(((width - 1) / 2.0 - p.getX()) * b.getWidth());
b.setTranslateY(-((height - 1) / 2.0 - p.getY()) * b.getHeight()); b.setTranslateY(-((height - 1) / 2.0 - p.getY()) * b.getHeight());
b.setTranslateZ((depth * length + thick) / 2.0); b.setTranslateZ((depth * length + thick) / 2.0);
})); }));
// BACK // BACK
getChildren().add(new Face(skin, start_x + width_interval + depth_interval * 2, start_y + depth_interval, width, height, interval, true, false, getChildren().add(new Face(skin, start_x + width_interval + depth_interval * 2, start_y + depth_interval, width, height, interval, true, false,
() -> new Box(length, length, thick), (b, p) -> { () -> new Box(length, length, thick), (b, p) -> {
b.setTranslateX(((width - 1) / 2.0 - p.getX()) * b.getWidth()); b.setTranslateX(((width - 1) / 2.0 - p.getX()) * b.getWidth());
b.setTranslateY(-((height - 1) / 2.0 - p.getY()) * b.getHeight()); b.setTranslateY(-((height - 1) / 2.0 - p.getY()) * b.getHeight());
b.setTranslateZ(-(depth * length + thick) / 2.0); b.setTranslateZ(-(depth * length + thick) / 2.0);
})); }));
// LEFT // LEFT
getChildren().add(new Face(skin, start_x + width_interval + depth_interval, start_y + depth_interval, depth, height, interval, false, false, getChildren().add(new Face(skin, start_x + width_interval + depth_interval, start_y + depth_interval, depth, height, interval, false, false,
() -> new Box(thick, length, length), (b, p) -> { () -> new Box(thick, length, length), (b, p) -> {
b.setTranslateX((width * length + thick) / 2.0); b.setTranslateX((width * length + thick) / 2.0);
b.setTranslateY(-((height - 1) / 2.0 - p.getY()) * b.getHeight()); b.setTranslateY(-((height - 1) / 2.0 - p.getY()) * b.getHeight());
b.setTranslateZ(((depth - 1) / 2.0 - p.getX()) * b.getDepth()); b.setTranslateZ(((depth - 1) / 2.0 - p.getX()) * b.getDepth());
})); }));
// RIGHT // RIGHT
getChildren().add(new Face(skin, start_x, start_y + depth_interval, depth, height, interval, true, false, getChildren().add(new Face(skin, start_x, start_y + depth_interval, depth, height, interval, true, false,
() -> new Box(thick, length, length), (b, p) -> { () -> new Box(thick, length, length), (b, p) -> {
b.setTranslateX(-(width * length + thick) / 2.0); b.setTranslateX(-(width * length + thick) / 2.0);
b.setTranslateY(-((height - 1) / 2.0 - p.getY()) * b.getHeight()); b.setTranslateY(-((height - 1) / 2.0 - p.getY()) * b.getHeight());
b.setTranslateZ(((depth - 1) / 2.0 - p.getX()) * b.getDepth()); b.setTranslateZ(((depth - 1) / 2.0 - p.getX()) * b.getDepth());
})); }));
// TOP // TOP
getChildren().add(new Face(skin, start_x + depth_interval, start_y, width, depth, interval, false, false, getChildren().add(new Face(skin, start_x + depth_interval, start_y, width, depth, interval, false, false,
() -> new Box(length, thick, length), (b, p) -> { () -> new Box(length, thick, length), (b, p) -> {
b.setTranslateX(((width - 1) / 2.0 - p.getX()) * b.getWidth()); b.setTranslateX(((width - 1) / 2.0 - p.getX()) * b.getWidth());
b.setTranslateY(-(height * length + thick) / 2.0); b.setTranslateY(-(height * length + thick) / 2.0);
b.setTranslateZ(-((depth - 1) / 2.0 - p.getY()) * b.getDepth()); b.setTranslateZ(-((depth - 1) / 2.0 - p.getY()) * b.getDepth());
})); }));
// BOTTOM // BOTTOM
getChildren().add(new Face(skin, start_x + width_interval + depth_interval, start_y, width, depth, interval, false, false, getChildren().add(new Face(skin, start_x + width_interval + depth_interval, start_y, width, depth, interval, false, false,
() -> new Box(length, thick, length), (b, p) -> { () -> new Box(length, thick, length), (b, p) -> {
b.setTranslateX(((width - 1) / 2.0 - p.getX()) * b.getWidth()); b.setTranslateX(((width - 1) / 2.0 - p.getX()) * b.getWidth());
b.setTranslateY((height * length + thick) / 2.0); b.setTranslateY((height * length + thick) / 2.0);
b.setTranslateZ(-((depth - 1) / 2.0 - p.getY()) * b.getDepth()); b.setTranslateZ(-((depth - 1) / 2.0 - p.getY()) * b.getDepth());
})); }));
} }
} }

View File

@ -1,42 +1,42 @@
package moe.mickey.minecraft.skin.fx; package moe.mickey.minecraft.skin.fx;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import javafx.animation.Transition; import javafx.animation.Transition;
import javafx.beans.value.WritableValue; import javafx.beans.value.WritableValue;
import javafx.util.Duration; import javafx.util.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
public class SkinTransition extends Transition { public class SkinTransition extends Transition {
protected Function<Double, Double> expression; protected Function<Double, Double> expression;
protected List<WritableValue<Number>> observables; protected List<WritableValue<Number>> observables;
protected boolean fix; protected boolean fix;
protected int count; protected int count;
public int getCount() { public int getCount() {
return count; return count;
} }
public SkinTransition(Duration duration, Function<Double, Double> expression, WritableValue<Number>... observables) { public SkinTransition(Duration duration, Function<Double, Double> expression, WritableValue<Number>... observables) {
setCycleDuration(duration); setCycleDuration(duration);
this.expression = expression; this.expression = expression;
this.observables = Arrays.asList(observables); this.observables = Arrays.asList(observables);
} }
@Override @Override
protected void interpolate(double frac) { protected void interpolate(double frac) {
if (frac == 0 || frac == 1) if (frac == 0 || frac == 1)
count++; count++;
double val = expression.apply(frac); double val = expression.apply(frac);
observables.forEach(w -> w.setValue(val)); observables.forEach(w -> w.setValue(val));
} }
@Override @Override
public void play() { public void play() {
count = 0; count = 0;
super.play(); super.play();
} }
} }

View File

@ -8,22 +8,22 @@ import moe.mickey.minecraft.skin.fx.SkinTransition;
public final class SkinAniRunning extends SkinAnimation { public final class SkinAniRunning extends SkinAnimation {
private SkinTransition larmTransition, rarmTransition; private SkinTransition larmTransition, rarmTransition;
public SkinAniRunning(int weight, int time, double angle, SkinCanvas canvas) { public SkinAniRunning(int weight, int time, double angle, SkinCanvas canvas) {
larmTransition = new SkinTransition(Duration.millis(time), larmTransition = new SkinTransition(Duration.millis(time),
v -> v * (larmTransition.getCount() % 4 < 2 ? 1 : -1) * angle, v -> v * (larmTransition.getCount() % 4 < 2 ? 1 : -1) * angle,
canvas.larm.getXRotate().angleProperty(), canvas.rleg.getXRotate().angleProperty()); canvas.larm.getXRotate().angleProperty(), canvas.rleg.getXRotate().angleProperty());
rarmTransition = new SkinTransition(Duration.millis(time), rarmTransition = new SkinTransition(Duration.millis(time),
v -> v * (rarmTransition.getCount() % 4 < 2 ? 1 : -1) * -angle, v -> v * (rarmTransition.getCount() % 4 < 2 ? 1 : -1) * -angle,
canvas.rarm.getXRotate().angleProperty(), canvas.lleg.getXRotate().angleProperty()); canvas.rarm.getXRotate().angleProperty(), canvas.lleg.getXRotate().angleProperty());
FunctionHelper.alwaysB(SkinTransition::setAutoReverse, true, larmTransition, rarmTransition); FunctionHelper.alwaysB(SkinTransition::setAutoReverse, true, larmTransition, rarmTransition);
FunctionHelper.alwaysB(SkinTransition::setCycleCount, 16, larmTransition, rarmTransition); FunctionHelper.alwaysB(SkinTransition::setCycleCount, 16, larmTransition, rarmTransition);
FunctionHelper.always(transitions::add, larmTransition, rarmTransition); FunctionHelper.always(transitions::add, larmTransition, rarmTransition);
this.weight = weight; this.weight = weight;
init(); init();
} }
} }

View File

@ -8,18 +8,18 @@ import moe.mickey.minecraft.skin.fx.SkinTransition;
public final class SkinAniWavingArms extends SkinAnimation { public final class SkinAniWavingArms extends SkinAnimation {
public SkinAniWavingArms(int weight, int time, double angle, SkinCanvas canvas) { public SkinAniWavingArms(int weight, int time, double angle, SkinCanvas canvas) {
SkinTransition larmTransition = new SkinTransition(Duration.millis(time), v -> v * angle, SkinTransition larmTransition = new SkinTransition(Duration.millis(time), v -> v * angle,
canvas.larm.getZRotate().angleProperty()); canvas.larm.getZRotate().angleProperty());
SkinTransition rarmTransition = new SkinTransition(Duration.millis(time), v -> v * -angle, SkinTransition rarmTransition = new SkinTransition(Duration.millis(time), v -> v * -angle,
canvas.rarm.getZRotate().angleProperty()); canvas.rarm.getZRotate().angleProperty());
FunctionHelper.alwaysB(SkinTransition::setAutoReverse, true, larmTransition, rarmTransition); FunctionHelper.alwaysB(SkinTransition::setAutoReverse, true, larmTransition, rarmTransition);
FunctionHelper.alwaysB(SkinTransition::setCycleCount, 2, larmTransition, rarmTransition); FunctionHelper.alwaysB(SkinTransition::setCycleCount, 2, larmTransition, rarmTransition);
FunctionHelper.always(transitions::add, larmTransition, rarmTransition); FunctionHelper.always(transitions::add, larmTransition, rarmTransition);
this.weight = weight; this.weight = weight;
init(); init();
} }
} }

View File

@ -22,17 +22,12 @@ import javafx.beans.property.StringProperty;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import javafx.scene.text.TextFlow; import javafx.scene.text.TextFlow;
import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.util.javafx.BindingMapping;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class HintPane extends VBox { public class HintPane extends VBox {
private final Text label = new Text(); private final Text label = new Text();

View File

@ -58,7 +58,6 @@ import org.jackhuang.hmcl.util.AggregatedObservableList;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.i18n.I18n;
import org.jackhuang.hmcl.util.javafx.BindingMapping; import org.jackhuang.hmcl.util.javafx.BindingMapping;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.io.File; import java.io.File;
import java.util.*; import java.util.*;

View File

@ -30,7 +30,6 @@ import javafx.scene.layout.VBox;
import org.jackhuang.hmcl.event.EventBus; import org.jackhuang.hmcl.event.EventBus;
import org.jackhuang.hmcl.event.RefreshingVersionsEvent; import org.jackhuang.hmcl.event.RefreshingVersionsEvent;
import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profile;
import org.jackhuang.hmcl.setting.Profiles; import org.jackhuang.hmcl.setting.Profiles;
import org.jackhuang.hmcl.ui.*; import org.jackhuang.hmcl.ui.*;
@ -40,11 +39,8 @@ import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.ui.profile.ProfileListItem; import org.jackhuang.hmcl.ui.profile.ProfileListItem;
import org.jackhuang.hmcl.ui.profile.ProfilePage; import org.jackhuang.hmcl.ui.profile.ProfilePage;
import org.jackhuang.hmcl.util.javafx.MappedObservableList; import org.jackhuang.hmcl.util.javafx.MappedObservableList;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;

View File

@ -17,7 +17,17 @@
*/ */
package org.jackhuang.hmcl.util; package org.jackhuang.hmcl.util;
import java.io.*; import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URLConnection; import java.net.URLConnection;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
@ -32,17 +42,9 @@ import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import static org.jackhuang.hmcl.util.Logging.LOG; import static org.jackhuang.hmcl.util.Logging.LOG;