Astra Client 1.3 (Skidded Client)
1) Interesting

Screenshot taken from the announments of channel of Resent's Discord
2) Source Code (Obfuscated Minecraft Class Names (I used a remapper to remap the unobfuscated MC class names to the obfuscated ones))
https://www.mediafire.com/file/k0fvgbfvfl4hlhv/Astra13.jar.src.zip/file
3) The skidding begins
Exhibit A:
Class astra.settings.BooleanSetting

Resent 3.8:

Exhibit B:
Class astra.settings.BooleanSetting

This is not inherently skidded, however, if you take a closer look, you come to see that BooleanSetting#enabled is a
static field. This shows about how much attention was given to the class when it was written (or skidded, but probably refactored.)
Exhibit C:
Package astra.ui.auth.openauth
Two things wrong here.
One, what is this package naming. Two, openauth is licensed under GPL-3.0. In distributing Astra Client, without
distributing said source, Astra Client is in direct violation of the GPL-3.0 license.
Two, for some reason, they have duplicated this package into astra.util.openauth. "Why?" is beyond me.
Exhibit D:
Package astra.util.font
Sub-exhibit 1:
Class astra.util.font.CFont



Augustus 2.6:



I would like to make a remark here.
The reason the Eaglercraft version of Astra Client does not have a custom font renderer is because
A) Eaglercraft runs on WebGL 2.0, which is based on OpenGLES 3.0 and
B) Augustus 2.6's CFont is programmed for OpenGL 1.1 (shown by the GL11# calls.) CFont uses the Fixed Function
Pipeline (glVertex* and glTexCoord calls) to render, whereas in OpenGLES 3.0, the Fixed Function Pipeline is
deprecated, and one must rewrite CFont to use the Shader Function Pipeline (which Astra Client has very obviously not done.)
Sub-exhibit 2:
Not exactly skidding, but a very interesting and roundabout way to load fonts indeed.
Method astra.util.font.FontUtils#bootstrap

Astute readers may see that this is a smart way to load all 3 sizes of the font at the same time iff
(if and only if) getFont was a blocking function. However, that is not the case. In fact, the unnecessary calls to
creating a new thread probably cause more performance deterioration when the JVM has to synchronize and create new threads.
The astute reader may incorrectly deduce that Font#createFont is actually a function that takes quite a long time to
run, so therefore getFont is a blocking function. However, this is only because Astra Client most likely skidded
this from somewhere, in which the slow method Font#createFont(int, InputStream) is used. It is sufficient to get
around this bottleneck in the getFont call by invoking Font#createFont(int, File) instead. Benchmark will be provided below.
Method astra.util.font.FontUtils#getFont

Two things to note.
One, the astute reader may have already recognized that the Map#containsKey call is in direct violation of
lax1dude's Eaglercraft code standards. See section 2 of EAGLERCRAFT_CODE_STANDARDS.md, which is located in the official Eaglercraft repository.
Two, what is the point of the basically empty new Thread() call (3rd new Thread call, the thread has no actual code inside)?
Benchmark code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | package astraclientbenchmarks.randomstuff;
import org.openjdk.jmh.annotations.*;
import java.awt.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Fork(value = 1)
@Warmup(iterations = 5, timeUnit = TimeUnit.MILLISECONDS, time = 5000)
@Measurement(iterations = 5, timeUnit = TimeUnit.MILLISECONDS, time = 5000)
@SuppressWarnings("unused")
public class AstraClientCodeBenchmark {
public static void main(String[] args) throws IOException {
org.openjdk.jmh.Main.main(args);
}
@SuppressWarnings({"unused", "UnusedAssignment"})
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void testProperCode() {
Font normal_, large_, medium_;
FontUtilMock.MinecraftFontRendererMock normal, large, medium;
Map<String, Font> locationMap1 = new HashMap<>();
normal_ = FontUtilMock.getFont(locationMap1, "src/test/resources/Poppins-Medium.ttf", 19, true);
Map<String, Font> locationMap2 = new HashMap<>();
large_ = FontUtilMock.getFont(locationMap2, "src/test/resources/Poppins-Medium.ttf", 45, true);
Map<String, Font> locationMap3 = new HashMap<>();
medium_ = FontUtilMock.getFont(locationMap3, "src/test/resources/Poppins-Medium.ttf", 30, true);
normal = new FontUtilMock.MinecraftFontRendererMock(normal_, true, true);
large = new FontUtilMock.MinecraftFontRendererMock(large_, true, true);
medium = new FontUtilMock.MinecraftFontRendererMock(medium_, true, true);
}
@Benchmark
@BenchmarkMode(Mode.AverageTime)
public void testShittyCode() {
FontUtilMock.bootstrap();
}
public static class FontUtilMock {
public static volatile int completed;
public static MinecraftFontRendererMock normal;
public static MinecraftFontRendererMock large;
public static MinecraftFontRendererMock medium;
private static Font normal_;
private static Font large_;
private static Font medium_;
public FontUtilMock() {
}
private static Font getFont(Map<String, Font> locationMap, String location, int size) {
return getFont(locationMap, location, size, false);
}
private static Font getFont(Map<String, Font> locationMap, String location, int size, boolean efficient) {
Font font = null;
try {
if (locationMap.containsKey(location)) {
font = ((Font)locationMap.get(location)).deriveFont(0, (float)size);
} else {
if (efficient) {
font = Font.createFont(0, new File(location));
} else {
// InputStream is = Minecraft.getMinecraft().getResourceManager().getResource(new ResourceLocation("Astra/font/" + location)).getInputStream();
InputStream is = new FileInputStream(location);
font = Font.createFont(0, is);
}
locationMap.put(location, font);
font = font.deriveFont(0, (float) size);
}
} catch (Exception var5) {
Exception e = var5;
e.printStackTrace();
System.out.println("Error loading font");
font = new Font("default", 0, 10);
}
return font;
}
public static boolean hasLoaded() {
return completed >= 3;
}
public static void bootstrap() {
(new Thread(() -> {
Map<String, Font> locationMap = new HashMap();
normal_ = getFont(locationMap, "src/test/resources/Poppins-Medium.ttf", 19);
++completed;
})).start();
(new Thread(() -> {
Map<String, Font> locationMap = new HashMap();
large_ = getFont(locationMap, "src/test/resources/Poppins-Medium.ttf", 45);
++completed;
})).start();
(new Thread(() -> {
new HashMap();
++completed;
})).start();
(new Thread(() -> {
Map<String, Font> locationMap = new HashMap();
medium_ = getFont(locationMap, "src/test/resources/Poppins-Medium.ttf", 30);
++completed;
})).start();
while(!hasLoaded()) {
try {
Thread.sleep(5L);
} catch (InterruptedException var1) {
InterruptedException e = var1;
e.printStackTrace();
}
}
normal = new MinecraftFontRendererMock(normal_, true, true);
large = new MinecraftFontRendererMock(large_, true, true);
medium = new MinecraftFontRendererMock(medium_, true, true);
}
public static class MinecraftFontRendererMock {
public MinecraftFontRendererMock(Font font, boolean bl1, boolean bl2) {}
}
}
}
|
Benchmark results:
Sub-exhibit 3
How does one skid the entire CFont class and mess this up:

Exhibit E:
Class astra.util.RoundedUtils
Method #drawSmoothRoundedRect

Resent 3.8:

Astra didn't even bother changing the names. If you're wondering why the Resent one looks different, that is because the
compiler produced brackets in the Astra decompilation whereas the Resent source does not contain brackets.
In fact, the entire class is basically all skidded from Resent 3.8, one can compare the sources if they want. I only
showed 1 method in this exhibit, as otherwise it would take too much space.
Exhibit F:
Class astra.Rainbow
Method #drawChromaString

Resent 3.8:

Astra didn't even bother changing the variable names.
Exhibit G:
Class astra.betterfps.BetterFpsHelper
Sub-exhibit 1:
Method #loadConfig

I didn't know Astra Client and SeaClient were spelled the same.......
Did I forget to mention that Astra's resources are in Astra/*?
Sub-exhibit 2:

Once again, "SeaClient" of all things.
Exhibit H:
Package astra.cosmetics
Sub-exhibit 1:
Class astra.cosmetics.CosmeticDragonWings$ModelDragonWings
Method #render

Quick google search of BLueDD lets us know that BLueDD is the developer of SeaClient.
Sub-exhibit 2:
Class astra.cosmetics.CapeChecker

Point of this? Makes no sense when you think about it deeper. Why Username? What's so special about that?
Also, the code is pretty bad, could be written more cleanly.
Sub-exhibit 3:
Class astra.cosmetics.CapeRenderer

From "your_mod_id", one can conclude that this class was most likely skidded from a Fabric/Forge mod/tutorial on how to make capes.
Exhibit I:
Package astra.hud.mod.impl
Sub-exhibit 1:
Class astra.hud.mod.impl.AutoGG
Method #handleChat

BlocksMC on Eaglercraft, checks out.
Sub-exhibit 2:
Class astra.hud.mod.impl.ModPotionStatus
Method #draw

Resent 3.8:

Note that the java compiler has optimized the i2 += l operation from the end of the loop into the update expression
of the for loop.
The local variable names match between Resent 3.8 and Astra Client, which shows that Astra Client's ModPotionStatus was
a copy and paste, plus modifications.
4) Closing
There is probably more that I missed in this quick look at Astra Client.