diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 6373e4bc..f3942d43 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -11,7 +11,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- - uses: actions/stale@main
+ - uses: actions/stale@v9
with:
stale-issue-message: |
This issue has been open for a while with no recent activity. If this issue is still important to you, please add a comment within the next 7 days to keep it open. Otherwise, the issue will be automatically closed to free up time for other tasks.
@@ -31,7 +31,9 @@ jobs:
- They have bugs or conflicts that won't be resolved
days-before-stale: 60
days-before-close: 7
+ exempt-issue-labels: "status:never-stale"
+ exempt-pr-labels: "status:never-stale"
stale-issue-label: "status:stale"
stale-pr-label: "status:stale"
- operations-per-run: 50
+ operations-per-run: 200
enable-statistics: true
diff --git a/codestyle/cleanup.xml b/codestyle/cleanup.xml
index 70a36773..a6aa8d3e 100644
--- a/codestyle/cleanup.xml
+++ b/codestyle/cleanup.xml
@@ -1,144 +1,147 @@
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/codestyle/formatter.xml b/codestyle/formatter.xml
index b44984ad..3c2ecb64 100644
--- a/codestyle/formatter.xml
+++ b/codestyle/formatter.xml
@@ -200,6 +200,7 @@
+
@@ -280,6 +281,7 @@
+
@@ -310,6 +312,7 @@
+
diff --git a/src/main/java/net/wurstclient/commands/PotionCmd.java b/src/main/java/net/wurstclient/commands/PotionCmd.java
index 5ecb99da..fd8ddc67 100644
--- a/src/main/java/net/wurstclient/commands/PotionCmd.java
+++ b/src/main/java/net/wurstclient/commands/PotionCmd.java
@@ -7,15 +7,15 @@
*/
package net.wurstclient.commands;
-import java.util.List;
+import java.util.ArrayList;
import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.item.ItemStack;
import net.minecraft.item.PotionItem;
-import net.minecraft.nbt.NbtCompound;
-import net.minecraft.nbt.NbtList;
+import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionUtil;
+import net.minecraft.potion.Potions;
import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier;
import net.minecraft.util.InvalidIdentifierException;
@@ -60,15 +60,18 @@ public final class PotionCmd extends Command
throw new CmdSyntaxError();
// get effects to start with
- NbtList effects;
+ ArrayList effects;
+ Potion potion;
switch(args[0].toLowerCase())
{
case "add":
- effects = convertEffectsToNbt(stack);
+ effects = new ArrayList<>(PotionUtil.getCustomPotionEffects(stack));
+ potion = PotionUtil.getPotion(stack);
break;
case "set":
- effects = new NbtList();
+ effects = new ArrayList<>();
+ potion = Potions.EMPTY;
break;
default:
@@ -78,96 +81,75 @@ public final class PotionCmd extends Command
// add new effects
for(int i = 0; i < (args.length - 1) / 3; i++)
{
- NbtCompound effect = new NbtCompound();
+ StatusEffect effect = parseEffect(args[1 + i * 3]);
+ int amplifier = parseInt(args[2 + i * 3]) - 1;
+ int duration = parseInt(args[3 + i * 3]) * 20;
- effect.putInt("Id", parseEffectId(args[1 + i * 3]));
- effect.putInt("Amplifier", parseInt(args[2 + i * 3]) - 1);
- effect.putInt("Duration", parseInt(args[3 + i * 3]) * 20);
-
- effects.add(effect);
+ effects.add(new StatusEffectInstance(effect, duration, amplifier));
}
- NbtCompound nbt = new NbtCompound();
- nbt.put("CustomPotionEffects", effects);
- stack.setNbt(nbt);
+ PotionUtil.setPotion(stack, potion);
+ setCustomPotionEffects(stack, effects);
ChatUtils.message("Potion modified.");
}
- private NbtList convertEffectsToNbt(ItemStack stack)
- {
- NbtList nbt = new NbtList();
- List effects =
- PotionUtil.getCustomPotionEffects(stack);
-
- for(StatusEffectInstance effect : effects)
- {
- NbtCompound tag = new NbtCompound();
-
- int id = StatusEffect.getRawId(effect.getEffectType());
- tag.putInt("Id", id);
- tag.putInt("Amplifier", effect.getAmplifier());
- tag.putInt("Duration", effect.getDuration());
-
- nbt.add(tag);
- }
-
- return nbt;
- }
-
private void remove(ItemStack stack, String[] args) throws CmdSyntaxError
{
if(args.length != 2)
throw new CmdSyntaxError();
- int id = parseEffectId(args[1]);
+ StatusEffect targetEffect = parseEffect(args[1]);
- List oldEffects =
- PotionUtil.getCustomPotionEffects(stack);
+ Potion oldPotion = PotionUtil.getPotion(stack);
+ boolean mainPotionContainsTargetEffect = oldPotion.getEffects().stream()
+ .anyMatch(effect -> effect.getEffectType() == targetEffect);
- NbtList newEffects = new NbtList();
- for(StatusEffectInstance oldEffect : oldEffects)
- {
- int oldId = StatusEffect.getRawId(oldEffect.getEffectType());
-
- if(oldId == id)
- continue;
-
- NbtCompound effect = new NbtCompound();
- effect.putInt("Id", oldId);
- effect.putInt("Amplifier", oldEffect.getAmplifier());
- effect.putInt("Duration", oldEffect.getDuration());
- newEffects.add(effect);
- }
+ ArrayList newEffects = new ArrayList<>();
+ if(mainPotionContainsTargetEffect)
+ PotionUtil.getPotionEffects(stack).forEach(newEffects::add);
+ else
+ PotionUtil.getCustomPotionEffects(stack).forEach(newEffects::add);
+ newEffects.removeIf(effect -> effect.getEffectType() == targetEffect);
- NbtCompound nbt = new NbtCompound();
- nbt.put("CustomPotionEffects", newEffects);
- stack.setNbt(nbt);
+ Potion newPotion =
+ mainPotionContainsTargetEffect ? Potions.EMPTY : oldPotion;
+
+ PotionUtil.setPotion(stack, newPotion);
+ setCustomPotionEffects(stack, newEffects);
ChatUtils.message("Effect removed.");
}
- private int parseEffectId(String input) throws CmdSyntaxError
+ private StatusEffect parseEffect(String input) throws CmdSyntaxError
{
- int id = 0;
+ StatusEffect effect;
if(MathUtils.isInteger(input))
- id = Integer.parseInt(input);
+ effect = Registries.STATUS_EFFECT.get(Integer.parseInt(input));
else
try
{
Identifier identifier = new Identifier(input);
- StatusEffect effect = Registries.STATUS_EFFECT.get(identifier);
-
- id = StatusEffect.getRawId(effect);
+ effect = Registries.STATUS_EFFECT.get(identifier);
}catch(InvalidIdentifierException e)
{
throw new CmdSyntaxError("Invalid effect: " + input);
}
- if(id < 1)
- throw new CmdSyntaxError();
+ if(effect == null)
+ throw new CmdSyntaxError("Invalid effect: " + input);
- return id;
+ return Registries.STATUS_EFFECT.getEntry(effect).value();
+ }
+
+ private void setCustomPotionEffects(ItemStack stack,
+ ArrayList effects)
+ {
+ // PotionUtil doesn't remove effects when passing an empty list to it
+ if(effects.isEmpty())
+ stack.removeSubNbt("CustomPotionEffects");
+ else
+ PotionUtil.setCustomPotionEffects(stack, effects);
}
private int parseInt(String s) throws CmdSyntaxError
diff --git a/src/main/java/net/wurstclient/hacks/AntiSpamHack.java b/src/main/java/net/wurstclient/hacks/AntiSpamHack.java
index efae48f6..16aed807 100644
--- a/src/main/java/net/wurstclient/hacks/AntiSpamHack.java
+++ b/src/main/java/net/wurstclient/hacks/AntiSpamHack.java
@@ -135,7 +135,17 @@ public final class AntiSpamHack extends Hack implements ChatInputListener
}
if(spamCounter > 1)
- event.setComponent(((MutableText)event.getComponent())
- .append(" [x" + spamCounter + "]"));
+ {
+ // Someone, somewhere, is creating a MutableText object with an
+ // immutable List siblings parameter, which causes the game to
+ // crash when calling append(). So we always have to create a new
+ // MutableText object to avoid that.
+ MutableText oldText = (MutableText)event.getComponent();
+ MutableText newText = MutableText.of(oldText.getContent());
+ newText.setStyle(oldText.getStyle());
+ oldText.getSiblings().forEach(newText::append);
+
+ event.setComponent(newText.append(" [x" + spamCounter + "]"));
+ }
}
}
diff --git a/src/main/java/net/wurstclient/hacks/AutoLibrarianHack.java b/src/main/java/net/wurstclient/hacks/AutoLibrarianHack.java
index 0f1345be..2f49d3c2 100644
--- a/src/main/java/net/wurstclient/hacks/AutoLibrarianHack.java
+++ b/src/main/java/net/wurstclient/hacks/AutoLibrarianHack.java
@@ -418,7 +418,7 @@ public final class AutoLibrarianHack extends Hack
for(TradeOffer tradeOffer : tradeOffers)
{
ItemStack stack = tradeOffer.getSellItem();
- if(!(stack.getItem() instanceof EnchantedBookItem book))
+ if(!(stack.getItem() instanceof EnchantedBookItem))
continue;
NbtList enchantmentNbt = EnchantedBookItem.getEnchantmentNbt(stack);
diff --git a/src/main/java/net/wurstclient/mixin/PlayerSkinProviderMixin.java b/src/main/java/net/wurstclient/mixin/PlayerSkinProviderMixin.java
index f2f64137..fadc762b 100644
--- a/src/main/java/net/wurstclient/mixin/PlayerSkinProviderMixin.java
+++ b/src/main/java/net/wurstclient/mixin/PlayerSkinProviderMixin.java
@@ -7,8 +7,6 @@
*/
package net.wurstclient.mixin;
-import java.io.InputStreamReader;
-import java.net.URL;
import java.util.HashMap;
import javax.annotation.Nullable;
@@ -22,8 +20,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.InsecurePublicKeyException;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
@@ -35,6 +31,8 @@ import net.minecraft.client.MinecraftClient;
import net.minecraft.client.texture.PlayerSkinProvider;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
+import net.wurstclient.util.json.JsonUtils;
+import net.wurstclient.util.json.WsonObject;
@Mixin(PlayerSkinProvider.class)
public abstract class PlayerSkinProviderMixin
@@ -43,7 +41,7 @@ public abstract class PlayerSkinProviderMixin
@Final
private MinecraftSessionService sessionService;
- private static JsonObject capes;
+ private static HashMap capes;
@Inject(at = @At("HEAD"),
method = "loadSkin(Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/texture/PlayerSkinProvider$SkinTextureAvailableCallback;Z)V",
@@ -116,14 +114,14 @@ public abstract class PlayerSkinProviderMixin
if(capes == null)
setupWurstCapes();
- if(capes.has(name))
+ if(capes.containsKey(name))
{
- String capeURL = capes.get(name).getAsString();
+ String capeURL = capes.get(name);
map.put(Type.CAPE, new MinecraftProfileTexture(capeURL, null));
- }else if(capes.has(uuid))
+ }else if(capes.containsKey(uuid))
{
- String capeURL = capes.get(uuid).getAsString();
+ String capeURL = capes.get(uuid);
map.put(Type.CAPE, new MinecraftProfileTexture(capeURL, null));
}
@@ -140,12 +138,11 @@ public abstract class PlayerSkinProviderMixin
{
try
{
- // TODO: download capes to file
- URL url = new URL("https://www.wurstclient.net/api/v1/capes.json");
+ // download cape list from wurstclient.net
+ WsonObject rawCapes = JsonUtils.parseURLToObject(
+ "https://www.wurstclient.net/api/v1/capes.json");
- capes =
- JsonParser.parseReader(new InputStreamReader(url.openStream()))
- .getAsJsonObject();
+ capes = rawCapes.getAllStrings();
}catch(Exception e)
{